diff --git a/CHANGELOG.md b/CHANGELOG.md index 58d0fb97e4f..b91e8f29150 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -188,6 +188,7 @@ Docs: https://docs.openclaw.ai - Tasks/maintenance: reconcile stale cron and chat-backed CLI task rows against live cron-job and agent-run ownership instead of treating any persisted session key as proof that the task is still running. (#60310) Thanks @lml2468. - Update/npm: prefer the npm binary that owns the installed global OpenClaw prefix so mixed Homebrew-plus-nvm setups update the right install. (#60153) Thanks @jayeshp19. - Windows/restart: clean up stale gateway listeners before Windows self-restart and treat listener and argv probe failures as inconclusive, so scheduled-task relaunch no longer falls into an `EADDRINUSE` retry loop. (#60480) Thanks @arifahmedjoy. +- Plugins: suppress trust-warning noise during non-activating snapshot and CLI metadata loads. (#61427) Thanks @gumadeiras. ## 2026.4.2 diff --git a/src/plugins/loader.cli-metadata.test.ts b/src/plugins/loader.cli-metadata.test.ts index b2af1e507dc..e5caefe3751 100644 --- a/src/plugins/loader.cli-metadata.test.ts +++ b/src/plugins/loader.cli-metadata.test.ts @@ -20,6 +20,51 @@ afterAll(() => { }); describe("plugin loader CLI metadata", () => { + it("suppresses trust warning logs during CLI metadata loads", async () => { + useNoBundledPlugins(); + const stateDir = makeTempDir(); + const globalDir = path.join(stateDir, "extensions", "rogue"); + fs.mkdirSync(globalDir, { recursive: true }); + writePlugin({ + id: "rogue", + dir: globalDir, + filename: "index.cjs", + body: `module.exports = { + id: "rogue", + register(api) { + api.registerCli(() => {}, { + descriptors: [ + { + name: "rogue", + description: "Rogue CLI metadata", + hasSubcommands: true, + }, + ], + }); + }, +};`, + }); + + const warnings: string[] = []; + const registry = await loadOpenClawPluginCliRegistry({ + env: { ...process.env, OPENCLAW_STATE_DIR: stateDir }, + logger: { + info: () => {}, + warn: (msg: string) => warnings.push(msg), + error: () => {}, + debug: () => {}, + }, + config: { + plugins: { + enabled: true, + }, + }, + }); + + expect(warnings).toEqual([]); + expect(registry.cliRegistrars.flatMap((entry) => entry.commands)).toContain("rogue"); + }); + it("passes validated plugin config into non-activating CLI metadata loads", async () => { useNoBundledPlugins(); const plugin = writePlugin({ diff --git a/src/plugins/loader.test.ts b/src/plugins/loader.test.ts index 3bb7015346f..cd7110d2f07 100644 --- a/src/plugins/loader.test.ts +++ b/src/plugins/loader.test.ts @@ -3682,6 +3682,43 @@ module.exports = { } }); + it("suppresses trust warning logs for non-activating snapshot loads", () => { + useNoBundledPlugins(); + const stateDir = makeTempDir(); + withEnv({ OPENCLAW_STATE_DIR: stateDir }, () => { + const globalDir = path.join(stateDir, "extensions", "rogue"); + mkdirSafe(globalDir); + writePlugin({ + id: "rogue", + body: simplePluginBody("rogue"), + dir: globalDir, + filename: "index.cjs", + }); + + const warnings: string[] = []; + const registry = loadOpenClawPlugins({ + activate: false, + cache: false, + logger: createWarningLogger(warnings), + config: { + plugins: { + enabled: true, + }, + }, + }); + + expect(warnings).toEqual([]); + expect( + registry.diagnostics.some( + (diag) => + diag.level === "warn" && + diag.pluginId === "rogue" && + diag.message.includes("loaded without install/load-path provenance"), + ), + ).toBe(true); + }); + }); + it("loads source TypeScript plugins that route through local runtime shims", () => { const plugin = writePlugin({ id: "source-runtime-shim", diff --git a/src/plugins/loader.ts b/src/plugins/loader.ts index 91149aeafde..dc11e3ed50c 100644 --- a/src/plugins/loader.ts +++ b/src/plugins/loader.ts @@ -876,12 +876,16 @@ function compareDuplicateCandidateOrder(params: { } function warnWhenAllowlistIsOpen(params: { + emitWarning: boolean; logger: PluginLogger; pluginsEnabled: boolean; allow: string[]; warningCacheKey: string; discoverablePlugins: Array<{ id: string; source: string; origin: PluginRecord["origin"] }>; }) { + if (!params.emitWarning) { + return; + } if (!params.pluginsEnabled) { return; } @@ -912,6 +916,7 @@ function warnAboutUntrackedLoadedPlugins(params: { registry: PluginRegistry; provenance: PluginProvenanceIndex; allowlist: string[]; + emitWarning: boolean; logger: PluginLogger; env: NodeJS.ProcessEnv; }) { @@ -941,7 +946,9 @@ function warnAboutUntrackedLoadedPlugins(params: { source: plugin.source, message, }); - params.logger.warn(`[plugins] ${plugin.id}: ${message} (${plugin.source})`); + if (params.emitWarning) { + params.logger.warn(`[plugins] ${plugin.id}: ${message} (${plugin.source})`); + } } } @@ -1110,6 +1117,7 @@ export function loadOpenClawPlugins(options: PluginLoadOptions = {}): PluginRegi }); pushDiagnostics(registry.diagnostics, manifestRegistry.diagnostics); warnWhenAllowlistIsOpen({ + emitWarning: shouldActivate, logger, pluginsEnabled: normalized.enabled, allow: normalized.allow, @@ -1608,6 +1616,7 @@ export function loadOpenClawPlugins(options: PluginLoadOptions = {}): PluginRegi registry, provenance, allowlist: normalized.allow, + emitWarning: shouldActivate, logger, env, }); @@ -1675,6 +1684,7 @@ export async function loadOpenClawPluginCliRegistry( }); pushDiagnostics(registry.diagnostics, manifestRegistry.diagnostics); warnWhenAllowlistIsOpen({ + emitWarning: false, logger, pluginsEnabled: normalized.enabled, allow: normalized.allow,