From 867b4c2a327c7a66c734ca85262c7fdfa23c8c5b Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sat, 25 Apr 2026 01:42:11 +0100 Subject: [PATCH] fix(plugins): log runtime deps staging progress --- CHANGELOG.md | 1 + src/plugins/loader.test.ts | 18 ++++++++++++++++++ src/plugins/loader.ts | 35 +++++++++++++++++++++++++++++++++-- 3 files changed, 52 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index aa805ba6732..c56879e1d50 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ Docs: https://docs.openclaw.ai ### Fixes +- Plugins/runtime deps: log bundled plugin runtime-dependency staging before synchronous npm installs start and include elapsed timing afterward, so first boot after upgrades no longer looks hung while dependencies are being repaired. - Agents/failover: forward embedded run abort signals into provider-owned model streams, cap implicit LLM idle watchdogs below long run timeouts, and mark 429 responses without usable retry timing as non-retryable so GitHub Copilot rate limits fail over or surface promptly instead of hanging until run timeout. Fixes #71120. - Plugins/Google Meet: make meeting creation join by default, with an explicit URL-only opt-out, so agents that create a Meet also enter it. - Telegram/polling: persist accepted update offsets before long-running handlers complete so poller restarts do not replay already-ingested updates, while keeping same-process retries for handler failures. diff --git a/src/plugins/loader.test.ts b/src/plugins/loader.test.ts index e75991ee27c..ab49fce0bd6 100644 --- a/src/plugins/loader.test.ts +++ b/src/plugins/loader.test.ts @@ -939,9 +939,16 @@ module.exports = { "utf-8", ); const installedSpecs: string[] = []; + const logger = { + info: vi.fn(), + warn: vi.fn(), + error: vi.fn(), + debug: vi.fn(), + }; const registry = loadOpenClawPlugins({ cache: false, + logger, config: { plugins: { enabled: true, @@ -953,6 +960,9 @@ module.exports = { }, }, bundledRuntimeDepsInstaller: ({ installRoot, missingSpecs }) => { + expect(logger.info).toHaveBeenCalledWith( + "[plugins] discord staging bundled runtime deps (1 missing, 1 install specs): discord-runtime@1.0.0", + ); installedSpecs.push(...missingSpecs); expect(fs.realpathSync(installRoot)).toBe(fs.realpathSync(plugin.dir)); fs.mkdirSync(path.join(installRoot, "node_modules", "discord-runtime"), { @@ -968,6 +978,11 @@ module.exports = { expect(installedSpecs).toEqual(["discord-runtime@1.0.0"]); expect(registry.plugins.find((entry) => entry.id === "discord")?.status).toBe("loaded"); + expect(logger.info).toHaveBeenCalledWith( + expect.stringMatching( + /^\[plugins\] discord installed bundled runtime deps in \d+ms: discord-runtime@1\.0\.0$/u, + ), + ); }); it("keeps bundled runtime dep install logs off non-activating loads", () => { @@ -1040,6 +1055,9 @@ module.exports = { expect(logger.info).not.toHaveBeenCalledWith( "[plugins] discord installed bundled runtime deps: discord-runtime@1.0.0", ); + expect(logger.info).not.toHaveBeenCalledWith( + "[plugins] discord staging bundled runtime deps (1 missing, 1 install specs): discord-runtime@1.0.0", + ); }); it("does not repair disabled bundled plugin runtime deps", () => { diff --git a/src/plugins/loader.ts b/src/plugins/loader.ts index d04ab2a632f..7196b44d860 100644 --- a/src/plugins/loader.ts +++ b/src/plugins/loader.ts @@ -34,6 +34,7 @@ import { buildPluginApi } from "./api-builder.js"; import { inspectBundleMcpRuntimeSupport } from "./bundle-mcp.js"; import { ensureBundledPluginRuntimeDeps, + installBundledRuntimeDeps, resolveBundledRuntimeDependencyInstallRoot, type BundledRuntimeDepsInstallParams, } from "./bundled-runtime-deps.js"; @@ -2325,6 +2326,8 @@ export function loadOpenClawPlugins(options: PluginLoadOptions = {}): PluginRegi candidate.origin === "bundled" && enableState.enabled ) { + let runtimeDepsInstallStartedAt: number | null = null; + let runtimeDepsInstallSpecs: string[] = []; try { const installRoot = resolveBundledRuntimeDependencyInstallRoot(pluginRoot, { env }); const retainSpecs = bundledRuntimeDepsRetainSpecsByInstallRoot.get(installRoot) ?? []; @@ -2334,7 +2337,26 @@ export function loadOpenClawPlugins(options: PluginLoadOptions = {}): PluginRegi env, config: cfg, retainSpecs, - installDeps: options.bundledRuntimeDepsInstaller, + installDeps: (installParams) => { + const installSpecs = installParams.installSpecs ?? installParams.missingSpecs; + runtimeDepsInstallStartedAt = Date.now(); + runtimeDepsInstallSpecs = installParams.missingSpecs; + if (shouldActivate) { + logger.info( + `[plugins] ${record.id} staging bundled runtime deps (${installParams.missingSpecs.length} missing, ${installSpecs.length} install specs): ${installParams.missingSpecs.join(", ")}`, + ); + } + const installer = + options.bundledRuntimeDepsInstaller ?? + ((params: BundledRuntimeDepsInstallParams) => + installBundledRuntimeDeps({ + installRoot: params.installRoot, + installExecutionRoot: params.installExecutionRoot, + missingSpecs: params.installSpecs ?? params.missingSpecs, + env, + })); + installer(installParams); + }, }); if (depsInstallResult.installedSpecs.length > 0) { bundledRuntimeDepsRetainSpecsByInstallRoot.set( @@ -2344,8 +2366,12 @@ export function loadOpenClawPlugins(options: PluginLoadOptions = {}): PluginRegi ), ); if (shouldActivate) { + const elapsed = + runtimeDepsInstallStartedAt === null + ? "" + : ` in ${Date.now() - runtimeDepsInstallStartedAt}ms`; logger.info( - `[plugins] ${record.id} installed bundled runtime deps: ${depsInstallResult.installedSpecs.join(", ")}`, + `[plugins] ${record.id} installed bundled runtime deps${elapsed}: ${depsInstallResult.installedSpecs.join(", ")}`, ); } } @@ -2371,6 +2397,11 @@ export function loadOpenClawPlugins(options: PluginLoadOptions = {}): PluginRegi ensureOpenClawPluginSdkAlias(path.dirname(path.dirname(pluginRoot))); } } catch (error) { + if (shouldActivate && runtimeDepsInstallStartedAt !== null) { + logger.error( + `[plugins] ${record.id} failed to stage bundled runtime deps after ${Date.now() - runtimeDepsInstallStartedAt}ms: ${runtimeDepsInstallSpecs.join(", ")}`, + ); + } pushPluginLoadError(`failed to install bundled runtime deps: ${String(error)}`); continue; }