From 73c429d24fdea788f045acb0824eb0fbba1d6692 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Fri, 1 May 2026 19:25:36 +0100 Subject: [PATCH] fix(release): stabilize plugin prerelease validation --- scripts/e2e/cron-mcp-cleanup-docker-client.ts | 25 ++++++++++++------- .../runtime-smoke.mjs | 2 +- .../e2e/lib/doctor-install-switch/scenario.sh | 3 ++- src/agents/subagent-registry-lifecycle.ts | 6 ++--- src/plugins/loader-records.test.ts | 14 +++++++++++ src/plugins/loader-records.ts | 3 ++- src/plugins/loader.ts | 4 +++ src/plugins/registry.ts | 4 ++- 8 files changed, 45 insertions(+), 16 deletions(-) diff --git a/scripts/e2e/cron-mcp-cleanup-docker-client.ts b/scripts/e2e/cron-mcp-cleanup-docker-client.ts index 364ee90685d..0969d48cf90 100644 --- a/scripts/e2e/cron-mcp-cleanup-docker-client.ts +++ b/scripts/e2e/cron-mcp-cleanup-docker-client.ts @@ -82,19 +82,26 @@ async function waitForProbeExit(params: { throw new Error(`${label} MCP probe process still alive after run: pid=${pid} args=${args}`); } -async function waitForAnyProbeExit(params: { +async function waitForAllProbeExits(params: { pidsPath: string; label: string; timeoutMs: number; -}): Promise { +}): Promise { const startedAt = Date.now(); let observed: number[] = []; while (Date.now() - startedAt < params.timeoutMs) { observed = await readProbePids(params.pidsPath); - for (const pid of observed) { - const args = await describeProbePid(pid); - if (!args || !args.includes("openclaw-cron-mcp-cleanup-probe")) { - return pid; + if (observed.length > 0) { + let allExited = true; + for (const pid of observed) { + const args = await describeProbePid(pid); + if (args?.includes("openclaw-cron-mcp-cleanup-probe")) { + allExited = false; + break; + } + } + if (allExited) { + return observed; } } await delay(100); @@ -201,7 +208,7 @@ async function runSubagentCleanupScenario(params: { pidPath: string; pidsPath: string; exitPath: string; -}): Promise<{ runId: string; exitedPid: number; pids: number[] }> { +}): Promise<{ runId: string; exitedPids: number[]; pids: number[] }> { const { gateway, pidPath, pidsPath, exitPath } = params; await resetProbeFiles({ pidPath, pidsPath, exitPath }); @@ -238,14 +245,14 @@ async function runSubagentCleanupScenario(params: { `subagent cleanup run did not finish ok: ${JSON.stringify(finished)}`, ); - const exitedPid = await waitForAnyProbeExit({ + const exitedPids = await waitForAllProbeExits({ pidsPath, label: "subagent", timeoutMs: 240_000, }); return { runId: run.runId, - exitedPid, + exitedPids, pids: await readProbePids(pidsPath), }; } diff --git a/scripts/e2e/lib/bundled-plugin-install-uninstall/runtime-smoke.mjs b/scripts/e2e/lib/bundled-plugin-install-uninstall/runtime-smoke.mjs index 758f13580fb..a1ae54238de 100644 --- a/scripts/e2e/lib/bundled-plugin-install-uninstall/runtime-smoke.mjs +++ b/scripts/e2e/lib/bundled-plugin-install-uninstall/runtime-smoke.mjs @@ -14,7 +14,7 @@ const READY_TIMEOUT_MS = readPositiveInt( const RPC_TIMEOUT_MS = readPositiveInt(process.env.OPENCLAW_BUNDLED_PLUGIN_RUNTIME_RPC_MS, 60000); const RPC_READY_TIMEOUT_MS = readPositiveInt( process.env.OPENCLAW_BUNDLED_PLUGIN_RUNTIME_RPC_READY_MS, - 90000, + 210000, ); function readPositiveInt(raw, fallback) { diff --git a/scripts/e2e/lib/doctor-install-switch/scenario.sh b/scripts/e2e/lib/doctor-install-switch/scenario.sh index faf572e5fca..07cb8d5c2a4 100644 --- a/scripts/e2e/lib/doctor-install-switch/scenario.sh +++ b/scripts/e2e/lib/doctor-install-switch/scenario.sh @@ -198,7 +198,8 @@ run_proxy_env_flow() { printf "%s\n" "Environment=HTTP_PROXY=http://stale-proxy.local:7890" printf "%s\n" "Environment=HTTPS_PROXY=https://stale-proxy.local:7890" } >>"$unit_path" - if ! timeout "$command_timeout" node "$git_cli" doctor --repair --yes >"$doctor_log" 2>&1; then + if ! timeout "$command_timeout" env OPENCLAW_UPDATE_IN_PROGRESS=1 \ + node "$git_cli" doctor --repair --force --yes --non-interactive >"$doctor_log" 2>&1; then cat "$doctor_log" exit 1 fi diff --git a/src/agents/subagent-registry-lifecycle.ts b/src/agents/subagent-registry-lifecycle.ts index 781885b86ba..fa7fd930118 100644 --- a/src/agents/subagent-registry-lifecycle.ts +++ b/src/agents/subagent-registry-lifecycle.ts @@ -441,7 +441,7 @@ export function createSubagentRegistryLifecycleController(params: { retryDeferredCompletedAnnounces(cleanupParams.runId); }; - const retireRunModeBundleMcpRuntime = (cleanupParams: { + const retireRunModeBundleMcpRuntime = async (cleanupParams: { runId: string; entry: SubagentRunRecord; reason: string; @@ -449,7 +449,7 @@ export function createSubagentRegistryLifecycleController(params: { if (cleanupParams.entry.spawnMode === "session") { return; } - void retireSessionMcpRuntimeForSessionKey({ + await retireSessionMcpRuntimeForSessionKey({ sessionKey: cleanupParams.entry.childSessionKey, reason: cleanupParams.reason, onError: (error, sessionId) => { @@ -772,7 +772,7 @@ export function createSubagentRegistryLifecycleController(params: { onWarn: (msg) => params.warn(msg, { runId: entry.runId }), }); - retireRunModeBundleMcpRuntime({ + await retireRunModeBundleMcpRuntime({ runId: completeParams.runId, entry, reason: "subagent-run-complete", diff --git a/src/plugins/loader-records.test.ts b/src/plugins/loader-records.test.ts index c0f25c30979..bb9f6a65e10 100644 --- a/src/plugins/loader-records.test.ts +++ b/src/plugins/loader-records.test.ts @@ -15,4 +15,18 @@ describe("plugin loader records", () => { expect(record.channelIds).toEqual(["kitchen-sink-channel"]); }); + + it("preserves manifest-declared provider ids before runtime registration", () => { + const record = createPluginRecord({ + id: "kitchen-sink", + name: "Kitchen Sink", + source: "/tmp/kitchen-sink/index.js", + origin: "global", + enabled: true, + providerIds: ["kitchen-sink-provider"], + configSchema: false, + }); + + expect(record.providerIds).toEqual(["kitchen-sink-provider"]); + }); }); diff --git a/src/plugins/loader-records.ts b/src/plugins/loader-records.ts index 76fa05ef25f..0073d9b7d63 100644 --- a/src/plugins/loader-records.ts +++ b/src/plugins/loader-records.ts @@ -23,6 +23,7 @@ export function createPluginRecord(params: { activationState?: PluginActivationState; syntheticAuthRefs?: string[]; channelIds?: readonly string[]; + providerIds?: readonly string[]; configSchema: boolean; contracts?: PluginManifestContracts; }): PluginRecord { @@ -50,7 +51,7 @@ export function createPluginRecord(params: { hookNames: [], channelIds: [...(params.channelIds ?? [])], cliBackendIds: [], - providerIds: [], + providerIds: [...(params.providerIds ?? [])], speechProviderIds: [], realtimeTranscriptionProviderIds: [], realtimeVoiceProviderIds: [], diff --git a/src/plugins/loader.ts b/src/plugins/loader.ts index a7e51880f02..c36c84eeca9 100644 --- a/src/plugins/loader.ts +++ b/src/plugins/loader.ts @@ -1460,6 +1460,7 @@ export function loadOpenClawPlugins(options: PluginLoadOptions = {}): PluginRegi activationState, syntheticAuthRefs: manifestRecord.syntheticAuthRefs, channelIds: manifestRecord.channels, + providerIds: manifestRecord.providers, configSchema: Boolean(manifestRecord.configSchema), contracts: manifestRecord.contracts, }); @@ -1496,6 +1497,7 @@ export function loadOpenClawPlugins(options: PluginLoadOptions = {}): PluginRegi activationState, syntheticAuthRefs: manifestRecord.syntheticAuthRefs, channelIds: manifestRecord.channels, + providerIds: manifestRecord.providers, configSchema: Boolean(manifestRecord.configSchema), contracts: manifestRecord.contracts, }); @@ -2305,6 +2307,7 @@ export async function loadOpenClawPluginCliRegistry( activationState, syntheticAuthRefs: manifestRecord.syntheticAuthRefs, channelIds: manifestRecord.channels, + providerIds: manifestRecord.providers, configSchema: Boolean(manifestRecord.configSchema), contracts: manifestRecord.contracts, }); @@ -2341,6 +2344,7 @@ export async function loadOpenClawPluginCliRegistry( activationState, syntheticAuthRefs: manifestRecord.syntheticAuthRefs, channelIds: manifestRecord.channels, + providerIds: manifestRecord.providers, configSchema: Boolean(manifestRecord.configSchema), contracts: manifestRecord.contracts, }); diff --git a/src/plugins/registry.ts b/src/plugins/registry.ts index cac720b8c1a..b38f32d83db 100644 --- a/src/plugins/registry.ts +++ b/src/plugins/registry.ts @@ -811,7 +811,9 @@ export function createPluginRegistry(registryParams: PluginRegistryParams) { }); return; } - record.providerIds.push(id); + if (!record.providerIds.includes(id)) { + record.providerIds.push(id); + } registry.providers.push({ pluginId: record.id, pluginName: record.name,