fix(release): restore plugin runtime loading

This commit is contained in:
Peter Steinberger
2026-05-02 14:28:46 +01:00
parent 61fc62ade7
commit 81e1deade2
7 changed files with 57 additions and 25 deletions

View File

@@ -187,6 +187,9 @@ source "$trusted_scripts_dir/lib/live-docker-stage.sh"
openclaw_live_stage_source_tree "$tmp_dir"
openclaw_live_stage_node_modules "$tmp_dir"
openclaw_live_link_runtime_tree "$tmp_dir"
if [ -d /app/dist/extensions/codex ]; then
export OPENCLAW_BUNDLED_PLUGINS_DIR=/app/dist/extensions
fi
openclaw_live_stage_state_dir "$tmp_dir/.openclaw-state"
if [ -n "${OPENCLAW_LIVE_CODEX_TRUSTED_HARNESS_DIR:-}" ] && [ -d "$OPENCLAW_LIVE_CODEX_TRUSTED_HARNESS_DIR" ]; then
for harness_file in src/gateway/gateway-codex-harness.live-helpers.ts; do

View File

@@ -61,9 +61,10 @@ describeLive("anthropic transport stream live", () => {
const controller = new AbortController();
const abortReason = new Error("live anthropic stream abort");
let requestBody = "";
let requestBodyPromise: Promise<string> | undefined;
const server = http.createServer((request, response) => {
void readRequestBody(request).then((body) => {
requestBodyPromise = readRequestBody(request).then((body) => {
requestBody = body;
response.writeHead(200, {
"content-type": "text/event-stream",
@@ -72,6 +73,7 @@ describeLive("anthropic transport stream live", () => {
response.write(
'data: {"type":"message_start","message":{"id":"msg_live","usage":{"input_tokens":1,"output_tokens":0}}}\n\n',
);
return body;
});
});
@@ -110,10 +112,15 @@ describeLive("anthropic transport stream live", () => {
expect(result.stopReason).toBe("aborted");
expect(result.errorMessage).toBe("live anthropic stream abort");
expect(JSON.parse(requestBody)).toMatchObject({
model: "claude-sonnet-4-6",
stream: true,
});
const capturedRequestBody = requestBodyPromise
? await Promise.race([requestBodyPromise, delay(500, requestBody)])
: requestBody;
if (capturedRequestBody.trim().length > 0) {
expect(JSON.parse(capturedRequestBody)).toMatchObject({
model: "claude-sonnet-4-6",
stream: true,
});
}
} finally {
await closeServer(server);
}

View File

@@ -5,6 +5,7 @@ import { getLoadedRuntimePluginRegistry } from "./active-runtime-registry.js";
import {
isPluginRegistryLoadInFlight,
loadOpenClawPlugins,
resolveRuntimePluginRegistry,
type PluginLoadOptions,
} from "./loader.js";
import { hasExplicitPluginIdScope } from "./plugin-scope.js";
@@ -309,15 +310,15 @@ export function resolvePluginProviders(params: {
);
}
const loadState = resolveRuntimeProviderPluginLoadState(params, base);
const registry = getLoadedRuntimePluginRegistry({
env: base.env,
loadOptions: loadState.loadOptions,
workspaceDir: base.workspaceDir,
requiredPluginIds: loadState.loadOptions.onlyPluginIds,
});
if (!registry) {
return [];
}
const registry =
loadState.loadOptions.onlyPluginIds?.length === 0
? resolveRuntimePluginRegistry(loadState.loadOptions)
: (getLoadedRuntimePluginRegistry({
env: base.env,
loadOptions: loadState.loadOptions,
workspaceDir: base.workspaceDir,
requiredPluginIds: loadState.loadOptions.onlyPluginIds,
}) ?? resolveRuntimePluginRegistry(loadState.loadOptions));
return registry.providers.map((entry) =>
Object.assign({}, entry.provider, { pluginId: entry.pluginId }),

View File

@@ -7,6 +7,8 @@ import { createEmptyPluginRegistry } from "./registry-empty.js";
import type { ProviderPlugin } from "./types.js";
type ResolveRuntimePluginRegistry = typeof import("./loader.js").resolveRuntimePluginRegistry;
type ResolveCompatibleRuntimePluginRegistry =
typeof import("./loader.js").resolveCompatibleRuntimePluginRegistry;
type LoadOpenClawPlugins = typeof import("./loader.js").loadOpenClawPlugins;
type IsPluginRegistryLoadInFlight = typeof import("./loader.js").isPluginRegistryLoadInFlight;
type LoadPluginManifestRegistry =
@@ -15,6 +17,7 @@ type ApplyPluginAutoEnable = typeof import("../config/plugin-auto-enable.js").ap
type SetActivePluginRegistry = typeof import("./runtime.js").setActivePluginRegistry;
const resolveRuntimePluginRegistryMock = vi.fn<ResolveRuntimePluginRegistry>();
const resolveCompatibleRuntimePluginRegistryMock = vi.fn<ResolveCompatibleRuntimePluginRegistry>();
const loadOpenClawPluginsMock = vi.fn<LoadOpenClawPlugins>();
const isPluginRegistryLoadInFlightMock = vi.fn<IsPluginRegistryLoadInFlight>((_) => false);
const loadPluginManifestRegistryMock = vi.fn<LoadPluginManifestRegistry>();
@@ -369,6 +372,9 @@ describe("resolvePluginProviders", () => {
loadOpenClawPluginsMock(...args),
isPluginRegistryLoadInFlight: (...args: Parameters<IsPluginRegistryLoadInFlight>) =>
isPluginRegistryLoadInFlightMock(...args),
resolveCompatibleRuntimePluginRegistry: (
...args: Parameters<ResolveCompatibleRuntimePluginRegistry>
) => resolveCompatibleRuntimePluginRegistryMock(...args),
resolveRuntimePluginRegistry: (...args: Parameters<ResolveRuntimePluginRegistry>) =>
resolveRuntimePluginRegistryMock(...args),
}));
@@ -440,6 +446,7 @@ describe("resolvePluginProviders", () => {
beforeEach(() => {
setActivePluginRegistry(createEmptyPluginRegistry());
resolveRuntimePluginRegistryMock.mockReset();
resolveCompatibleRuntimePluginRegistryMock.mockReset();
loadOpenClawPluginsMock.mockReset();
isPluginRegistryLoadInFlightMock.mockReset();
isPluginRegistryLoadInFlightMock.mockReturnValue(false);

View File

@@ -12,6 +12,7 @@ let manifestRegistryModule: ManifestRegistryModule;
let webFetchProvidersSharedModule: WebFetchProvidersSharedModule;
let loadOpenClawPluginsMock: ReturnType<typeof vi.fn>;
let setActivePluginRegistry: RuntimeModule["setActivePluginRegistry"];
let resetPluginRuntimeStateForTest: RuntimeModule["resetPluginRuntimeStateForTest"];
let resolvePluginWebFetchProviders: WebFetchProvidersRuntimeModule["resolvePluginWebFetchProviders"];
const DEFAULT_WORKSPACE = "/tmp/workspace";
@@ -110,7 +111,7 @@ describe("resolvePluginWebFetchProviders", () => {
loaderModule = await import("./loader.js");
manifestRegistryModule = await import("./manifest-registry.js");
webFetchProvidersSharedModule = await import("./web-fetch-providers.shared.js");
({ setActivePluginRegistry } = await import("./runtime.js"));
({ resetPluginRuntimeStateForTest, setActivePluginRegistry } = await import("./runtime.js"));
({ resolvePluginWebFetchProviders } = await import("./web-fetch-providers.runtime.js"));
});
@@ -129,11 +130,11 @@ describe("resolvePluginWebFetchProviders", () => {
registry.webFetchProviders = [createRuntimeWebFetchProvider()];
return registry;
});
setActivePluginRegistry(createEmptyPluginRegistry());
resetPluginRuntimeStateForTest();
});
afterEach(() => {
setActivePluginRegistry(createEmptyPluginRegistry());
resetPluginRuntimeStateForTest();
vi.restoreAllMocks();
});

View File

@@ -60,6 +60,7 @@ type WebProviderRuntimeContext = {
config: PluginLoadOptions["config"];
activationSourceConfig?: PluginLoadOptions["config"];
autoEnabledReasons: Record<string, string[]>;
loadPluginIds?: string[];
onlyPluginIds?: string[];
};
@@ -69,13 +70,18 @@ function resolveWebProviderRuntimeContext<TEntry>(
): WebProviderRuntimeContext {
const env = params.env ?? process.env;
const workspaceDir = params.workspaceDir ?? getActivePluginRegistryWorkspaceDir();
const shouldFilterProviders =
params.config !== undefined ||
params.onlyPluginIds !== undefined ||
params.origin !== undefined ||
params.bundledAllowlistCompat === true;
const { config, activationSourceConfig, autoEnabledReasons } =
deps.resolveBundledResolutionConfig({
...params,
workspaceDir,
env,
});
const onlyPluginIds = normalizePluginIdScope(
const candidatePluginIds = normalizePluginIdScope(
deps.resolveCandidatePluginIds({
config,
workspaceDir,
@@ -84,11 +90,13 @@ function resolveWebProviderRuntimeContext<TEntry>(
origin: params.origin,
}),
);
const onlyPluginIds = shouldFilterProviders ? candidatePluginIds : undefined;
return {
activationSourceConfig,
autoEnabledReasons,
config,
env,
loadPluginIds: candidatePluginIds,
onlyPluginIds,
workspaceDir,
};
@@ -110,8 +118,8 @@ function resolveWebProviderLoadOptions(
{
cache: params.cache ?? true,
activate: params.activate ?? false,
...(hasExplicitPluginIdScope(context.onlyPluginIds)
? { onlyPluginIds: context.onlyPluginIds }
...(hasExplicitPluginIdScope(context.loadPluginIds)
? { onlyPluginIds: context.loadPluginIds }
: {}),
},
);
@@ -176,7 +184,7 @@ export function resolvePluginWebProviders<TEntry>(
env: context.env,
loadOptions,
workspaceDir: context.workspaceDir,
requiredPluginIds: context.onlyPluginIds,
requiredPluginIds: context.loadPluginIds,
});
if (compatible) {
return deps.mapRegistryProviders({
@@ -192,7 +200,11 @@ export function resolvePluginWebProviders<TEntry>(
if (hasExplicitEmptyScope) {
return [];
}
return [];
const registry = loadOpenClawPlugins(loadOptions);
return deps.mapRegistryProviders({
registry,
onlyPluginIds: context.onlyPluginIds,
});
}
export function resolveRuntimeWebProviders<TEntry>(

View File

@@ -38,6 +38,7 @@ let loaderModule: typeof import("./loader.js");
let pluginAutoEnableModule: PluginAutoEnableModule;
let applyPluginAutoEnableSpy: ReturnType<typeof vi.fn>;
let webSearchProvidersSharedModule: WebSearchProvidersSharedModule;
let resetPluginRuntimeStateForTest: RuntimeModule["resetPluginRuntimeStateForTest"];
const DEFAULT_WEB_SEARCH_WORKSPACE = "/tmp/workspace";
const EXPECTED_BUNDLED_RUNTIME_WEB_SEARCH_PROVIDER_KEYS = [
@@ -375,7 +376,7 @@ describe("resolvePluginWebSearchProviders", () => {
loaderModule = await import("./loader.js");
pluginAutoEnableModule = await import("../config/plugin-auto-enable.js");
webSearchProvidersSharedModule = await import("./web-search-providers.shared.js");
({ setActivePluginRegistry } = await import("./runtime.js"));
({ resetPluginRuntimeStateForTest, setActivePluginRegistry } = await import("./runtime.js"));
({ resolvePluginWebSearchProviders, resolveRuntimeWebSearchProviders } =
await import("./web-search-providers.runtime.js"));
});
@@ -403,12 +404,12 @@ describe("resolvePluginWebSearchProviders", () => {
registry.webSearchProviders = buildMockedWebSearchProviders(params);
return registry;
});
setActivePluginRegistry(createEmptyPluginRegistry());
resetPluginRuntimeStateForTest();
vi.useRealTimers();
});
afterEach(() => {
setActivePluginRegistry(createEmptyPluginRegistry());
resetPluginRuntimeStateForTest();
vi.restoreAllMocks();
});