

























@@ -3415,13 +3415,107 @@ module.exports = { id: "throws-after-import", register() {} };`,
34153415);
34163416});
341734173418-it("throws when activate:false is used without cache:false", () => {
3419-expect(() => loadOpenClawPlugins({ activate: false })).toThrow(
3420-"activate:false requires cache:false",
3421-);
3422-expect(() => loadOpenClawPlugins({ activate: false, cache: true })).toThrow(
3423-"activate:false requires cache:false",
3424-);
3418+it("uses discovery registration mode for non-activating loads", () => {
3419+useNoBundledPlugins();
3420+const marker = "__openclawDiscoveryModeTest";
3421+const plugin = writePlugin({
3422+id: "discovery-mode-test",
3423+filename: "discovery-mode-test.cjs",
3424+body: `module.exports = {
3425+ id: "discovery-mode-test",
3426+ register(api) {
3427+ globalThis.${marker} = globalThis.${marker} || [];
3428+ globalThis.${marker}.push(api.registrationMode);
3429+ api.registerProvider({ id: "discovery-provider", label: "Discovery Provider", auth: [] });
3430+ api.registerTool({
3431+ name: "discovery_tool",
3432+ description: "Discovery tool",
3433+ parameters: {},
3434+ execute: async () => ({ content: [{ type: "text", text: "ok" }] }),
3435+ });
3436+ },
3437+ };`,
3438+});
3439+const config = {
3440+plugins: {
3441+load: { paths: [plugin.file] },
3442+allow: ["discovery-mode-test"],
3443+},
3444+};
3445+3446+const snapshot = loadOpenClawPlugins({
3447+activate: false,
3448+cache: false,
3449+workspaceDir: plugin.dir,
3450+ config,
3451+});
3452+expect((globalThis as Record<string, unknown>)[marker]).toEqual(["discovery"]);
3453+expect(snapshot.providers.map((entry) => entry.provider.id)).toEqual(["discovery-provider"]);
3454+expect(snapshot.tools.flatMap((entry) => entry.names)).toContain("discovery_tool");
3455+3456+loadOpenClawPlugins({
3457+cache: false,
3458+workspaceDir: plugin.dir,
3459+ config,
3460+});
3461+expect((globalThis as Record<string, unknown>)[marker]).toEqual(["discovery", "full"]);
3462+delete (globalThis as Record<string, unknown>)[marker];
3463+});
3464+3465+it("caches non-activating snapshots without restoring global side effects", () => {
3466+useNoBundledPlugins();
3467+clearPluginCommands();
3468+const marker = "__openclawSnapshotCacheRegisterCount";
3469+const plugin = writePlugin({
3470+id: "snapshot-cache",
3471+filename: "snapshot-cache.cjs",
3472+body: `module.exports = {
3473+ id: "snapshot-cache",
3474+ register(api) {
3475+ globalThis.${marker} = (globalThis.${marker} || 0) + 1;
3476+ api.registerCommand({
3477+ name: "snapshot-command",
3478+ description: "Snapshot command",
3479+ handler: async () => ({ text: "ok" }),
3480+ });
3481+ },
3482+ };`,
3483+});
3484+const options = {
3485+activate: false,
3486+workspaceDir: plugin.dir,
3487+config: {
3488+plugins: {
3489+load: { paths: [plugin.file] },
3490+allow: ["snapshot-cache"],
3491+},
3492+},
3493+onlyPluginIds: ["snapshot-cache"],
3494+};
3495+3496+const first = loadOpenClawPlugins(options);
3497+const second = loadOpenClawPlugins(options);
3498+3499+expect(second).toBe(first);
3500+expect((globalThis as Record<string, unknown>)[marker]).toBe(1);
3501+expect(first.commands.map((entry) => entry.command.name)).toEqual(["snapshot-command"]);
3502+expect(getPluginCommandSpecs()).toEqual([]);
3503+3504+const active = loadOpenClawPlugins({
3505+workspaceDir: plugin.dir,
3506+config: options.config,
3507+onlyPluginIds: ["snapshot-cache"],
3508+});
3509+expect(active).not.toBe(first);
3510+expect((globalThis as Record<string, unknown>)[marker]).toBe(2);
3511+expect(getPluginCommandSpecs()).toEqual([
3512+{
3513+name: "snapshot-command",
3514+description: "Snapshot command",
3515+acceptsArgs: false,
3516+},
3517+]);
3518+delete (globalThis as Record<string, unknown>)[marker];
34253519});
3426352034273521it("re-initializes global hook runner when serving registry from cache", () => {
@@ -4061,7 +4155,7 @@ module.exports = { id: "throws-after-import", register() {} };`,
40614155},
40624156},
40634157{
4064-label: "rejects duplicate channel ids during plugin registration",
4158+label: "updates duplicate channel ids during same-plugin registration",
40654159pluginId: "channel-dup",
40664160body: `module.exports = { id: "channel-dup", register(api) {
40674161 api.registerChannel({
@@ -4103,11 +4197,9 @@ module.exports = { id: "throws-after-import", register() {} };`,
41034197} };`,
41044198assert: (registry: ReturnType<typeof loadOpenClawPlugins>) => {
41054199expect(registry.channels.filter((entry) => entry.plugin.id === "demo")).toHaveLength(1);
4106-expectRegistryErrorDiagnostic({
4107- registry,
4108-pluginId: "channel-dup",
4109-message: "channel already registered: demo (channel-dup)",
4110-});
4200+expect(
4201+registry.channels.find((entry) => entry.plugin.id === "demo")?.plugin.meta?.label,
4202+).toBe("Demo Duplicate");
41114203},
41124204},
41134205{
@@ -4417,14 +4509,14 @@ module.exports = { id: "throws-after-import", register() {} };`,
44174509},
44184510},
44194511{
4420-label: "same plugin can replace its own route",
4512+label: "same plugin can implicitly replace its own route",
44214513buildPlugins: () => [
44224514writePlugin({
44234515id: "http-route-replace-self",
44244516filename: "http-route-replace-self.cjs",
44254517body: `module.exports = { id: "http-route-replace-self", register(api) {
44264518 api.registerHttpRoute({ path: "/demo", auth: "plugin", handler: async () => false });
4427- api.registerHttpRoute({ path: "/demo", auth: "plugin", replaceExisting: true, handler: async () => true });
4519+ api.registerHttpRoute({ path: "/demo", auth: "plugin", handler: async () => true });
44284520} };`,
44294521}),
44304522],
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。