fix(plugins): cache cli registration entries

This commit is contained in:
Peter Steinberger
2026-05-02 10:40:01 +01:00
parent 70dcd81f03
commit 3b3def0354
3 changed files with 51 additions and 11 deletions

View File

@@ -30,6 +30,7 @@ Docs: https://docs.openclaw.ai
### Fixes
- Plugins/CLI: cache plugin CLI registration entries per command program so completion state generation does not repeat the full plugin sweep in one invocation. Thanks @ScientificProgrammer.
- Plugins: reuse gateway-bindable plugin loader cache entries for later default-mode loads without serving default-built registries to gateway-bound requests, reducing repeated plugin registration during dispatch. Refs #61756. Thanks @DmitryPogodaev.
- Gateway/secrets: include the caught error message in `secrets.reload` and `secrets.resolve` warning logs while keeping RPC errors generic, so operators can diagnose reload and permission failures. Thanks @davidangularme.
- Anthropic-compatible streams: recover text deltas that arrive before their matching content block, so Kimi Code and similar providers do not finish as empty `incomplete_result` replies. Fixes #76007. Thanks @vliuyt.

View File

@@ -197,6 +197,26 @@ describe("registerPluginCliCommands", () => {
);
});
it("reuses loaded plugin CLI entries on repeat calls for the same program", async () => {
const program = createProgram();
await registerPluginCliCommands(program, {} as OpenClawConfig);
await registerPluginCliCommands(program, {} as OpenClawConfig);
expect(mocks.loadOpenClawPlugins).toHaveBeenCalledTimes(1);
});
it("reloads plugin CLI entries when the requested primary command changes", async () => {
const program = createProgram();
await registerPluginCliCommands(program, {} as OpenClawConfig, undefined, undefined, {
primary: "memory",
});
await registerPluginCliCommands(program, {} as OpenClawConfig);
expect(mocks.loadOpenClawPlugins).toHaveBeenCalledTimes(2);
});
it("loads plugin CLI commands from the auto-enabled config snapshot", async () => {
const { rawConfig, autoEnabledConfig } = createAutoEnabledCliFixture();
mocks.applyPluginAutoEnable.mockReturnValue({

View File

@@ -17,6 +17,19 @@ type RegisterPluginCliOptions = {
primary?: string | null;
};
type PluginCliRegistrationEntries = Awaited<
ReturnType<typeof loadPluginCliRegistrationEntriesWithDefaults>
>;
const PLUGIN_CLI_ENTRIES_CACHE_KEY = Symbol.for("openclaw.plugin-cli-registration-entries-cache");
interface ProgramWithEntriesCache {
[PLUGIN_CLI_ENTRIES_CACHE_KEY]?: {
primary: string | undefined;
entries: PluginCliRegistrationEntries;
};
}
const logger = createPluginCliLogger();
export const loadValidatedConfigForPluginRegistration =
@@ -46,21 +59,27 @@ export async function registerPluginCliCommands(
const mode = options?.mode ?? "eager";
const primary = options?.primary ?? undefined;
await registerPluginCliCommandGroups(
program,
await loadPluginCliRegistrationEntriesWithDefaults({
const programWithCache = program as Command & ProgramWithEntriesCache;
const cached = programWithCache[PLUGIN_CLI_ENTRIES_CACHE_KEY];
let entries: PluginCliRegistrationEntries;
if (cached && cached.primary === primary) {
entries = cached.entries;
} else {
entries = await loadPluginCliRegistrationEntriesWithDefaults({
cfg,
env,
loaderOptions,
primaryCommand: primary,
}),
{
mode,
primary,
existingCommands: new Set(program.commands.map((cmd) => cmd.name())),
logger,
},
);
});
programWithCache[PLUGIN_CLI_ENTRIES_CACHE_KEY] = { primary, entries };
}
await registerPluginCliCommandGroups(program, entries, {
mode,
primary,
existingCommands: new Set(program.commands.map((cmd) => cmd.name())),
logger,
});
}
export async function registerPluginCliCommandsFromValidatedConfig(