mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 12:20:44 +00:00
refactor: reuse startup plugin metadata snapshot
This commit is contained in:
@@ -8,6 +8,7 @@ Docs: https://docs.openclaw.ai
|
||||
|
||||
- Channels/Yuanbao: register the Tencent Yuanbao external channel plugin (`openclaw-plugin-yuanbao`) in the official channel catalog, contract suites, and community plugin docs, with a new `docs/channels/yuanbao.md` quick-start guide for WebSocket bot DMs and group chats. (#72756) Thanks @loongfay.
|
||||
- Channels/QQBot: add full group chat support (history tracking, @-mention gating, activation modes, per-group config, FIFO message queue with deliver debounce), C2C `stream_messages` streaming with a `StreamingController` lifecycle manager, unified `sendMedia` with chunked upload for large files, and refactor the engine into pipeline stages, focused outbound submodules, builtin slash-command modules, and explicit DI ports via `createEngineAdapters()`. (#70624) Thanks @cxyhhhhh.
|
||||
- Gateway/startup: pass the plugin metadata snapshot from config validation into plugin bootstrap so startup reuses one manifest product instead of rebuilding plugin metadata. Thanks @shakkernerd.
|
||||
|
||||
## 2026.4.26
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ import fs from "node:fs";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import JSON5 from "json5";
|
||||
import { resolveAgentWorkspaceDir, resolveDefaultAgentId } from "../agents/agent-scope-config.js";
|
||||
import { ensureOwnerDisplaySecret } from "../agents/owner-display.js";
|
||||
import { applyRuntimeLegacyConfigMigrations } from "../commands/doctor/shared/runtime-compat-api.js";
|
||||
import { loadDotEnv } from "../infra/dotenv.js";
|
||||
@@ -23,6 +24,10 @@ import {
|
||||
resolveInstalledPluginIndexRecordsStorePath,
|
||||
writePersistedInstalledPluginIndexInstallRecordsSync,
|
||||
} from "../plugins/installed-plugin-index-records.js";
|
||||
import {
|
||||
loadPluginMetadataSnapshot,
|
||||
type PluginMetadataSnapshot,
|
||||
} from "../plugins/plugin-metadata-snapshot.js";
|
||||
import { sanitizeTerminalText } from "../terminal/safe-text.js";
|
||||
import { isRecord } from "../utils.js";
|
||||
import { VERSION } from "../version.js";
|
||||
@@ -1166,6 +1171,7 @@ function createConfigFileSnapshot(params: {
|
||||
issues: ConfigFileSnapshot["issues"];
|
||||
warnings: ConfigFileSnapshot["warnings"];
|
||||
legacyIssues: LegacyConfigIssue[];
|
||||
pluginMetadataSnapshot?: PluginMetadataSnapshot;
|
||||
}): ConfigFileSnapshot {
|
||||
const sourceConfig = asResolvedSourceConfig(params.sourceConfig);
|
||||
const runtimeConfig = asRuntimeConfig(params.runtimeConfig);
|
||||
@@ -1183,6 +1189,9 @@ function createConfigFileSnapshot(params: {
|
||||
issues: params.issues,
|
||||
warnings: params.warnings,
|
||||
legacyIssues: params.legacyIssues,
|
||||
...(params.pluginMetadataSnapshot
|
||||
? { pluginMetadataSnapshot: params.pluginMetadataSnapshot }
|
||||
: {}),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1745,10 +1754,24 @@ export function createConfigIO(
|
||||
? hashConfigRaw(installMigration.persistedRootRaw)
|
||||
: hash;
|
||||
fallbackSourceConfig = coerceConfig(effectiveConfigRaw);
|
||||
let pluginMetadataSnapshot: PluginMetadataSnapshot | undefined;
|
||||
const loadValidationPluginMetadataSnapshot = (config: OpenClawConfig) => {
|
||||
if (pluginMetadataSnapshot) {
|
||||
return pluginMetadataSnapshot;
|
||||
}
|
||||
const defaultAgentId = resolveDefaultAgentId(config);
|
||||
pluginMetadataSnapshot = loadPluginMetadataSnapshot({
|
||||
config,
|
||||
workspaceDir: resolveAgentWorkspaceDir(config, defaultAgentId),
|
||||
env: deps.env,
|
||||
});
|
||||
return pluginMetadataSnapshot;
|
||||
};
|
||||
const validated = await deps.measure("config.snapshot.read.validate", () =>
|
||||
validateConfigObjectWithPlugins(effectiveConfigRaw, {
|
||||
env: deps.env,
|
||||
pluginValidation: overrides.pluginValidation,
|
||||
loadPluginMetadataSnapshot: loadValidationPluginMetadataSnapshot,
|
||||
}),
|
||||
);
|
||||
if (!validated.ok) {
|
||||
@@ -1789,6 +1812,7 @@ export function createConfigIO(
|
||||
issues: [],
|
||||
warnings: [...validated.warnings, ...envVarWarnings],
|
||||
legacyIssues: legacyResolution.sourceLegacyIssues,
|
||||
pluginMetadataSnapshot,
|
||||
}),
|
||||
envSnapshotForRestore: readResolution.envSnapshotForRestore,
|
||||
}),
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import type { PluginMetadataSnapshot } from "../plugins/plugin-metadata-snapshot.js";
|
||||
import type {
|
||||
SilentReplyPolicyShape,
|
||||
SilentReplyRewriteShape,
|
||||
@@ -184,4 +185,5 @@ export type ConfigFileSnapshot = {
|
||||
issues: ConfigValidationIssue[];
|
||||
warnings: ConfigValidationIssue[];
|
||||
legacyIssues: LegacyConfigIssue[];
|
||||
pluginMetadataSnapshot?: PluginMetadataSnapshot;
|
||||
};
|
||||
|
||||
@@ -239,6 +239,7 @@ describe("validateConfigObjectWithPlugins bundled allowlist compatibility", () =
|
||||
const result = validateConfigObjectWithPlugins(
|
||||
{
|
||||
plugins: {
|
||||
allow: ["opik"],
|
||||
entries: {
|
||||
opik: {
|
||||
enabled: true,
|
||||
@@ -261,4 +262,30 @@ describe("validateConfigObjectWithPlugins bundled allowlist compatibility", () =
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
it("loads a plugin metadata snapshot once during plugin validation", () => {
|
||||
const loadPluginMetadataSnapshot = vi.fn((_config: unknown) => ({
|
||||
manifestRegistry: createPluginConfigSchemaRegistry(),
|
||||
}));
|
||||
|
||||
const result = validateConfigObjectWithPlugins(
|
||||
{
|
||||
plugins: {
|
||||
allow: ["opik"],
|
||||
entries: {
|
||||
opik: {
|
||||
enabled: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
loadPluginMetadataSnapshot,
|
||||
},
|
||||
);
|
||||
|
||||
expect(result.ok).toBe(true);
|
||||
expect(loadPluginMetadataSnapshot).toHaveBeenCalledOnce();
|
||||
expect(mockLoadPluginManifestRegistry).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -711,6 +711,9 @@ type ValidateConfigWithPluginsParams = {
|
||||
env?: NodeJS.ProcessEnv;
|
||||
pluginValidation?: "full" | "skip";
|
||||
pluginMetadataSnapshot?: Pick<PluginMetadataSnapshot, "manifestRegistry">;
|
||||
loadPluginMetadataSnapshot?: (
|
||||
config: OpenClawConfig,
|
||||
) => Pick<PluginMetadataSnapshot, "manifestRegistry">;
|
||||
};
|
||||
|
||||
export function validateConfigObjectWithPlugins(
|
||||
@@ -722,6 +725,7 @@ export function validateConfigObjectWithPlugins(
|
||||
env: params?.env,
|
||||
pluginValidation: params?.pluginValidation ?? "full",
|
||||
pluginMetadataSnapshot: params?.pluginMetadataSnapshot,
|
||||
loadPluginMetadataSnapshot: params?.loadPluginMetadataSnapshot,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -734,6 +738,7 @@ export function validateConfigObjectRawWithPlugins(
|
||||
env: params?.env,
|
||||
pluginValidation: params?.pluginValidation ?? "full",
|
||||
pluginMetadataSnapshot: params?.pluginMetadataSnapshot,
|
||||
loadPluginMetadataSnapshot: params?.loadPluginMetadataSnapshot,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -810,6 +815,11 @@ function validateConfigObjectWithPluginsBase(
|
||||
};
|
||||
|
||||
const loadValidationRegistry = (): RegistryInfo => {
|
||||
const pluginMetadataSnapshot = opts.loadPluginMetadataSnapshot?.(config);
|
||||
if (pluginMetadataSnapshot) {
|
||||
registryInfo = { registry: pluginMetadataSnapshot.manifestRegistry };
|
||||
return registryInfo;
|
||||
}
|
||||
const workspaceDir = resolveAgentWorkspaceDir(config, resolveDefaultAgentId(config));
|
||||
const registry = loadPluginManifestRegistryForPluginRegistry({
|
||||
config,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import type { OpenClawConfig } from "../config/types.openclaw.js";
|
||||
import type { PluginMetadataSnapshot } from "../plugins/plugin-metadata-snapshot.js";
|
||||
|
||||
const applyPluginAutoEnable = vi.hoisted(() =>
|
||||
vi.fn((params: { config: unknown }) => ({
|
||||
@@ -22,6 +23,45 @@ const resolveBundledRuntimeDependencyPackageInstallRoot = vi.hoisted(() =>
|
||||
vi.fn((_packageRoot: string, _params: unknown) => "/runtime"),
|
||||
);
|
||||
const pluginManifestRegistry = vi.hoisted(() => ({ plugins: [], diagnostics: [] }));
|
||||
const pluginMetadataSnapshot = vi.hoisted(
|
||||
(): PluginMetadataSnapshot => ({
|
||||
index: {
|
||||
version: 1,
|
||||
hostContractVersion: "test",
|
||||
compatRegistryVersion: "test",
|
||||
migrationVersion: 1,
|
||||
policyHash: "policy",
|
||||
generatedAtMs: 0,
|
||||
installRecords: {},
|
||||
plugins: [],
|
||||
diagnostics: [],
|
||||
},
|
||||
registryDiagnostics: [],
|
||||
manifestRegistry: pluginManifestRegistry,
|
||||
plugins: [],
|
||||
diagnostics: [],
|
||||
byPluginId: new Map(),
|
||||
normalizePluginId: (pluginId) => pluginId,
|
||||
owners: {
|
||||
channels: new Map(),
|
||||
channelConfigs: new Map(),
|
||||
providers: new Map(),
|
||||
modelCatalogProviders: new Map(),
|
||||
cliBackends: new Map(),
|
||||
setupProviders: new Map(),
|
||||
commandAliases: new Map(),
|
||||
contracts: new Map(),
|
||||
},
|
||||
metrics: {
|
||||
registrySnapshotMs: 0,
|
||||
manifestRegistryMs: 0,
|
||||
ownerMapsMs: 0,
|
||||
totalMs: 0,
|
||||
indexPluginCount: 0,
|
||||
manifestPluginCount: 0,
|
||||
},
|
||||
}),
|
||||
);
|
||||
const pluginLookUpTableMetrics = vi.hoisted(() => ({
|
||||
registrySnapshotMs: 0,
|
||||
manifestRegistryMs: 0,
|
||||
@@ -259,6 +299,7 @@ describe("prepareGatewayPluginBootstrap runtime-deps staging", () => {
|
||||
cfgAtStart: runtimeConfig,
|
||||
activationSourceConfig: sourceConfig,
|
||||
startupRuntimeConfig: runtimeConfig,
|
||||
pluginMetadataSnapshot,
|
||||
minimalTestGateway: false,
|
||||
log,
|
||||
});
|
||||
@@ -270,6 +311,7 @@ describe("prepareGatewayPluginBootstrap runtime-deps staging", () => {
|
||||
expect(loadPluginLookUpTable).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
activationSourceConfig: sourceConfig,
|
||||
metadataSnapshot: pluginMetadataSnapshot,
|
||||
config: expect.objectContaining({
|
||||
channels: expect.objectContaining({
|
||||
telegram: expect.objectContaining({
|
||||
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
scanBundledPluginRuntimeDeps,
|
||||
} from "../plugins/bundled-runtime-deps.js";
|
||||
import { loadPluginLookUpTable } from "../plugins/plugin-lookup-table.js";
|
||||
import type { PluginMetadataSnapshot } from "../plugins/plugin-metadata-snapshot.js";
|
||||
import { createEmptyPluginRegistry } from "../plugins/registry.js";
|
||||
import { getActivePluginRegistry, setActivePluginRegistry } from "../plugins/runtime.js";
|
||||
import { mergeActivationSectionsIntoRuntimeConfig } from "./plugin-activation-runtime-config.js";
|
||||
@@ -95,6 +96,7 @@ export async function prepareGatewayPluginBootstrap(params: {
|
||||
cfgAtStart: OpenClawConfig;
|
||||
activationSourceConfig?: OpenClawConfig;
|
||||
startupRuntimeConfig: OpenClawConfig;
|
||||
pluginMetadataSnapshot?: PluginMetadataSnapshot;
|
||||
minimalTestGateway: boolean;
|
||||
log: GatewayPluginBootstrapLog;
|
||||
}) {
|
||||
@@ -149,6 +151,7 @@ export async function prepareGatewayPluginBootstrap(params: {
|
||||
workspaceDir: defaultWorkspaceDir,
|
||||
env: process.env,
|
||||
activationSourceConfig,
|
||||
metadataSnapshot: params.pluginMetadataSnapshot,
|
||||
});
|
||||
const deferredConfiguredChannelPluginIds = [
|
||||
...(pluginLookUpTable?.startup.configuredDeferredChannelPluginIds ?? []),
|
||||
|
||||
@@ -411,6 +411,7 @@ export async function startGatewayServer(
|
||||
cfgAtStart,
|
||||
activationSourceConfig: startupActivationSourceConfig,
|
||||
startupRuntimeConfig,
|
||||
pluginMetadataSnapshot: configSnapshot.pluginMetadataSnapshot,
|
||||
minimalTestGateway,
|
||||
log,
|
||||
}),
|
||||
|
||||
Reference in New Issue
Block a user