mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 07:30:43 +00:00
refactor: route plugin metadata consumers through snapshots
This commit is contained in:
@@ -10,8 +10,7 @@ import {
|
||||
normalizePluginsConfigWithResolver,
|
||||
resolveEffectivePluginActivationState,
|
||||
} from "../plugins/config-policy.js";
|
||||
import type { PluginManifestRegistry } from "../plugins/manifest-registry.js";
|
||||
import { loadPluginManifestRegistryForPluginRegistry } from "../plugins/plugin-registry.js";
|
||||
import { loadPluginMetadataSnapshot } from "../plugins/plugin-metadata-snapshot.js";
|
||||
import { isRecord } from "../utils.js";
|
||||
import { loadEmbeddedPiMcpConfig } from "./embedded-pi-mcp.js";
|
||||
|
||||
@@ -69,33 +68,6 @@ function loadBundleSettingsFile(params: {
|
||||
}
|
||||
}
|
||||
|
||||
function buildRegistryPluginIdAliases(
|
||||
registry: PluginManifestRegistry,
|
||||
): Readonly<Record<string, string>> {
|
||||
return Object.fromEntries(
|
||||
registry.plugins
|
||||
.flatMap((record) => [
|
||||
...(record.providers ?? [])
|
||||
.filter((providerId) => providerId !== record.id)
|
||||
.map((providerId) => [providerId, record.id] as const),
|
||||
...(record.legacyPluginIds ?? []).map(
|
||||
(legacyPluginId) => [legacyPluginId, record.id] as const,
|
||||
),
|
||||
])
|
||||
.toSorted(([left], [right]) => left.localeCompare(right)),
|
||||
);
|
||||
}
|
||||
|
||||
function createRegistryPluginIdNormalizer(
|
||||
registry: PluginManifestRegistry,
|
||||
): (id: string) => string {
|
||||
const aliases = buildRegistryPluginIdAliases(registry);
|
||||
return (id: string) => {
|
||||
const trimmed = id.trim();
|
||||
return aliases[trimmed] ?? trimmed;
|
||||
};
|
||||
}
|
||||
|
||||
export function loadEnabledBundlePiSettingsSnapshot(params: {
|
||||
cwd: string;
|
||||
cfg?: OpenClawConfig;
|
||||
@@ -104,18 +76,19 @@ export function loadEnabledBundlePiSettingsSnapshot(params: {
|
||||
if (!workspaceDir) {
|
||||
return {};
|
||||
}
|
||||
const registry = loadPluginManifestRegistryForPluginRegistry({
|
||||
const metadataSnapshot = loadPluginMetadataSnapshot({
|
||||
workspaceDir,
|
||||
config: params.cfg,
|
||||
includeDisabled: true,
|
||||
config: params.cfg ?? {},
|
||||
env: process.env,
|
||||
});
|
||||
const registry = metadataSnapshot.manifestRegistry;
|
||||
if (registry.plugins.length === 0) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const normalizedPlugins = normalizePluginsConfigWithResolver(
|
||||
params.cfg?.plugins,
|
||||
createRegistryPluginIdNormalizer(registry),
|
||||
metadataSnapshot.normalizePluginId,
|
||||
);
|
||||
let snapshot: PiSettingsSnapshot = {};
|
||||
|
||||
|
||||
@@ -79,6 +79,42 @@ vi.mock("../plugins/plugin-registry.js", async () => {
|
||||
};
|
||||
});
|
||||
|
||||
vi.mock("../plugins/plugin-metadata-snapshot.js", async () => {
|
||||
const fs = await import("node:fs");
|
||||
const path = await import("node:path");
|
||||
const loadRegistry = (params: { workspaceDir?: string }) => {
|
||||
const rootDir = path.join(
|
||||
params.workspaceDir ?? "",
|
||||
".openclaw",
|
||||
"extensions",
|
||||
"claude-bundle",
|
||||
);
|
||||
if (!fs.existsSync(path.join(rootDir, ".claude-plugin", "plugin.json"))) {
|
||||
return { plugins: [], diagnostics: [] };
|
||||
}
|
||||
const resolvedRootDir = fs.realpathSync(rootDir);
|
||||
return {
|
||||
diagnostics: [],
|
||||
plugins: [
|
||||
{
|
||||
id: "claude-bundle",
|
||||
origin: "workspace",
|
||||
format: "bundle",
|
||||
bundleFormat: "claude",
|
||||
settingsFiles: ["settings.json"],
|
||||
rootDir: resolvedRootDir,
|
||||
},
|
||||
],
|
||||
};
|
||||
};
|
||||
return {
|
||||
loadPluginMetadataSnapshot: (params: { workspaceDir?: string }) => ({
|
||||
manifestRegistry: loadRegistry(params),
|
||||
normalizePluginId: (id: string) => id.trim(),
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
vi.mock("./embedded-pi-mcp.js", async () => {
|
||||
const fs = await import("node:fs");
|
||||
const path = await import("node:path");
|
||||
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
type PluginManifestRecord,
|
||||
type PluginManifestRegistry,
|
||||
} from "../plugins/manifest-registry.js";
|
||||
import { loadPluginManifestRegistryForPluginRegistry } from "../plugins/plugin-registry.js";
|
||||
import { loadPluginMetadataSnapshot } from "../plugins/plugin-metadata-snapshot.js";
|
||||
import { resolveOwningPluginIdsForModelRef } from "../plugins/providers.js";
|
||||
import { resolvePluginSetupAutoEnableReasons } from "../plugins/setup-registry.js";
|
||||
import { normalizeOptionalLowercaseString } from "../shared/string-coerce.js";
|
||||
@@ -220,16 +220,13 @@ function resolvePluginsWithOwnedToolConfig(
|
||||
|
||||
function resolvePluginIdForConfiguredWebFetchProvider(
|
||||
providerId: string | undefined,
|
||||
env: NodeJS.ProcessEnv,
|
||||
registry: PluginManifestRegistry,
|
||||
): string | undefined {
|
||||
const normalizedProviderId = normalizeOptionalLowercaseString(providerId);
|
||||
if (!normalizedProviderId) {
|
||||
return undefined;
|
||||
}
|
||||
return loadPluginManifestRegistryForPluginRegistry({
|
||||
env,
|
||||
includeDisabled: true,
|
||||
}).plugins.find(
|
||||
return registry.plugins.find(
|
||||
(plugin) =>
|
||||
plugin.origin === "bundled" &&
|
||||
(plugin.contracts?.webFetchProviders ?? []).some(
|
||||
@@ -623,7 +620,7 @@ export function resolveConfiguredPluginAutoEnableCandidates(params: {
|
||||
: undefined;
|
||||
const webFetchPluginId = resolvePluginIdForConfiguredWebFetchProvider(
|
||||
webFetchProvider,
|
||||
params.env,
|
||||
params.registry,
|
||||
);
|
||||
if (webFetchPluginId) {
|
||||
changes.push({
|
||||
@@ -869,11 +866,10 @@ export function resolvePluginAutoEnableManifestRegistry(params: {
|
||||
return (
|
||||
params.manifestRegistry ??
|
||||
(configMayNeedPluginManifestRegistry(params.config, params.env)
|
||||
? loadPluginManifestRegistryForPluginRegistry({
|
||||
? loadPluginMetadataSnapshot({
|
||||
config: params.config,
|
||||
env: params.env,
|
||||
includeDisabled: true,
|
||||
})
|
||||
}).manifestRegistry
|
||||
: EMPTY_PLUGIN_MANIFEST_REGISTRY)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -34,6 +34,12 @@ vi.mock("../plugins/plugin-registry.js", () => ({
|
||||
mockLoadPluginManifestRegistry(...args),
|
||||
}));
|
||||
|
||||
vi.mock("../plugins/plugin-metadata-snapshot.js", () => ({
|
||||
loadPluginMetadataSnapshot: (...args: unknown[]) => ({
|
||||
manifestRegistry: mockLoadPluginManifestRegistry(...args),
|
||||
}),
|
||||
}));
|
||||
|
||||
vi.mock("../plugins/current-plugin-metadata-snapshot.js", () => ({
|
||||
getCurrentPluginMetadataSnapshot: (...args: unknown[]) =>
|
||||
mockGetCurrentPluginMetadataSnapshot(...args),
|
||||
@@ -309,7 +315,7 @@ describe("loadGatewayRuntimeConfigSchema", () => {
|
||||
|
||||
expect(mockLoadPluginManifestRegistry).toHaveBeenCalledTimes(3);
|
||||
for (const call of mockLoadPluginManifestRegistry.mock.calls) {
|
||||
expect(call[0]).toMatchObject({ includeDisabled: true });
|
||||
expect(call[0]).toHaveProperty("config");
|
||||
expect(call[0]).not.toHaveProperty("bundledChannelConfigCollector");
|
||||
}
|
||||
expect(getActivePluginRegistry()).toBe(activeRegistry);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { resolveAgentWorkspaceDir, resolveDefaultAgentId } from "../agents/agent-scope.js";
|
||||
import { getCurrentPluginMetadataSnapshot } from "../plugins/current-plugin-metadata-snapshot.js";
|
||||
import { loadPluginManifestRegistryForPluginRegistry } from "../plugins/plugin-registry.js";
|
||||
import { loadPluginMetadataSnapshot } from "../plugins/plugin-metadata-snapshot.js";
|
||||
import {
|
||||
collectChannelSchemaMetadata,
|
||||
collectPluginSchemaMetadata,
|
||||
@@ -15,14 +15,11 @@ function loadManifestRegistry(config: OpenClawConfig, env?: NodeJS.ProcessEnv) {
|
||||
if (currentSnapshot) {
|
||||
return currentSnapshot.manifestRegistry;
|
||||
}
|
||||
return loadPluginManifestRegistryForPluginRegistry({
|
||||
return loadPluginMetadataSnapshot({
|
||||
config,
|
||||
// Bundled channel schemas are already generated into the base schema; avoid
|
||||
// loading plugin config-schema modules on every config.get/config.schema.
|
||||
env,
|
||||
env: env ?? process.env,
|
||||
workspaceDir,
|
||||
includeDisabled: true,
|
||||
});
|
||||
}).manifestRegistry;
|
||||
}
|
||||
|
||||
export function loadGatewayRuntimeConfigSchema(): ConfigSchemaResponse {
|
||||
|
||||
@@ -120,12 +120,23 @@ vi.mock("../plugins/plugin-registry.js", () => ({
|
||||
loadPluginManifestRegistryForPluginRegistry: () => mockLoadPluginManifestRegistry(),
|
||||
}));
|
||||
|
||||
vi.mock("../plugins/plugin-metadata-snapshot.js", () => ({
|
||||
loadPluginMetadataSnapshot: () => ({
|
||||
manifestRegistry: mockLoadPluginManifestRegistry(),
|
||||
}),
|
||||
}));
|
||||
|
||||
vi.mock("../plugins/doctor-contract-registry.js", () => ({
|
||||
collectRelevantDoctorPluginIds: () => [],
|
||||
listPluginDoctorLegacyConfigRules: () => [],
|
||||
applyPluginDoctorCompatibilityMigrations: () => ({ next: null, changes: [] }),
|
||||
}));
|
||||
|
||||
vi.mock("../secrets/target-registry-data.js", () => ({
|
||||
getCoreSecretTargetRegistry: () => [],
|
||||
getSecretTargetRegistry: () => [],
|
||||
}));
|
||||
|
||||
vi.mock("../channels/plugins/legacy-config.js", () => ({
|
||||
collectChannelLegacyConfigRules: () => [],
|
||||
}));
|
||||
|
||||
@@ -16,8 +16,10 @@ import {
|
||||
import { loadInstalledPluginIndexInstallRecordsSync } from "../plugins/installed-plugin-index-record-reader.js";
|
||||
import { resolveManifestCommandAliasOwnerInRegistry } from "../plugins/manifest-command-aliases.js";
|
||||
import type { PluginManifestRegistry } from "../plugins/manifest-registry.js";
|
||||
import type { PluginMetadataSnapshot } from "../plugins/plugin-metadata-snapshot.js";
|
||||
import { loadPluginManifestRegistryForPluginRegistry } from "../plugins/plugin-registry.js";
|
||||
import {
|
||||
loadPluginMetadataSnapshot,
|
||||
type PluginMetadataSnapshot,
|
||||
} from "../plugins/plugin-metadata-snapshot.js";
|
||||
import { validateJsonSchemaValue } from "../plugins/schema-validator.js";
|
||||
import { hasKind } from "../plugins/slots.js";
|
||||
import { collectLegacySecretRefEnvMarkerCandidates } from "../secrets/legacy-secretref-env-marker.js";
|
||||
@@ -821,12 +823,11 @@ function validateConfigObjectWithPluginsBase(
|
||||
return registryInfo;
|
||||
}
|
||||
const workspaceDir = resolveAgentWorkspaceDir(config, resolveDefaultAgentId(config));
|
||||
const registry = loadPluginManifestRegistryForPluginRegistry({
|
||||
const registry = loadPluginMetadataSnapshot({
|
||||
config,
|
||||
workspaceDir: workspaceDir ?? undefined,
|
||||
env: opts.env,
|
||||
includeDisabled: true,
|
||||
});
|
||||
env: opts.env ?? process.env,
|
||||
}).manifestRegistry;
|
||||
registryInfo = { registry };
|
||||
return registryInfo;
|
||||
};
|
||||
|
||||
@@ -2,7 +2,7 @@ import { z } from "zod";
|
||||
import { collectBundledChannelConfigs } from "../plugins/bundled-channel-config-metadata.js";
|
||||
import type { PluginManifestRegistry } from "../plugins/manifest-registry.js";
|
||||
import type { PluginManifest } from "../plugins/manifest.js";
|
||||
import { loadPluginManifestRegistryForPluginRegistry } from "../plugins/plugin-registry.js";
|
||||
import { loadPluginMetadataSnapshot } from "../plugins/plugin-metadata-snapshot.js";
|
||||
import type { ChannelsConfig } from "./types.channels.js";
|
||||
import { ChannelHeartbeatVisibilitySchema } from "./zod-schema.channels.js";
|
||||
import { ContextVisibilityModeSchema, GroupPolicySchema } from "./zod-schema.core.js";
|
||||
@@ -87,9 +87,7 @@ function normalizeBundledChannelConfigs(
|
||||
let next: ChannelsConfig | undefined;
|
||||
let registry: PluginManifestRegistry | undefined;
|
||||
for (const channelId of Object.keys(value)) {
|
||||
registry ??= loadPluginManifestRegistryForPluginRegistry({
|
||||
includeDisabled: true,
|
||||
});
|
||||
registry ??= loadPluginMetadataSnapshot({ config: {}, env: process.env }).manifestRegistry;
|
||||
const runtimeSchema = getDirectChannelRuntimeSchema(channelId, registry);
|
||||
if (!runtimeSchema) {
|
||||
continue;
|
||||
|
||||
@@ -4,6 +4,7 @@ const pluginRegistryMocks = vi.hoisted(() => ({
|
||||
loadPluginManifestRegistryForInstalledIndex: vi.fn(),
|
||||
loadPluginManifestRegistryForPluginRegistry: vi.fn(),
|
||||
loadPluginRegistrySnapshot: vi.fn(() => ({ plugins: [] })),
|
||||
loadPluginMetadataSnapshot: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("./manifest-registry-installed.js", () => ({
|
||||
@@ -23,6 +24,14 @@ vi.mock("../plugins/plugin-registry.js", () => ({
|
||||
loadPluginRegistrySnapshot: pluginRegistryMocks.loadPluginRegistrySnapshot,
|
||||
}));
|
||||
|
||||
vi.mock("./plugin-metadata-snapshot.js", () => ({
|
||||
loadPluginMetadataSnapshot: pluginRegistryMocks.loadPluginMetadataSnapshot,
|
||||
}));
|
||||
|
||||
vi.mock("../plugins/plugin-metadata-snapshot.js", () => ({
|
||||
loadPluginMetadataSnapshot: pluginRegistryMocks.loadPluginMetadataSnapshot,
|
||||
}));
|
||||
|
||||
vi.resetModules();
|
||||
|
||||
const {
|
||||
@@ -53,6 +62,10 @@ function setManifestPlugins(plugins: Array<Record<string, unknown>>) {
|
||||
pluginRegistryMocks.loadPluginManifestRegistryForPluginRegistry.mockReturnValue({
|
||||
plugins,
|
||||
});
|
||||
pluginRegistryMocks.loadPluginMetadataSnapshot.mockReturnValue({
|
||||
plugins,
|
||||
manifestRegistry: { plugins },
|
||||
});
|
||||
}
|
||||
|
||||
function expectResolvedProviderAuthChoices(params: {
|
||||
@@ -88,6 +101,11 @@ describe("provider auth choice manifest helpers", () => {
|
||||
});
|
||||
pluginRegistryMocks.loadPluginRegistrySnapshot.mockReset();
|
||||
pluginRegistryMocks.loadPluginRegistrySnapshot.mockReturnValue({ plugins: [] });
|
||||
pluginRegistryMocks.loadPluginMetadataSnapshot.mockReset();
|
||||
pluginRegistryMocks.loadPluginMetadataSnapshot.mockReturnValue({
|
||||
plugins: [],
|
||||
manifestRegistry: { plugins: [] },
|
||||
});
|
||||
resetProviderAuthAliasMapCacheForTest();
|
||||
});
|
||||
|
||||
@@ -572,7 +590,7 @@ describe("provider auth choice manifest helpers", () => {
|
||||
]);
|
||||
|
||||
const resolvedProviderId = resolveProviderIdForAuth("fixture-provider-plan");
|
||||
expect(pluginRegistryMocks.loadPluginManifestRegistryForPluginRegistry).toHaveBeenCalled();
|
||||
expect(pluginRegistryMocks.loadPluginMetadataSnapshot).toHaveBeenCalled();
|
||||
expect(resolvedProviderId).toBe("fixture-provider");
|
||||
expect(
|
||||
resolveManifestProviderApiKeyChoice({
|
||||
|
||||
@@ -3,8 +3,8 @@ import type { OpenClawConfig } from "../config/types.openclaw.js";
|
||||
import { sanitizeForLog } from "../terminal/ansi.js";
|
||||
import { normalizePluginsConfig, resolveEffectiveEnableState } from "./config-state.js";
|
||||
import type { PluginManifestRecord } from "./manifest-registry.js";
|
||||
import { loadPluginMetadataSnapshot } from "./plugin-metadata-snapshot.js";
|
||||
import type { PluginOrigin } from "./plugin-origin.types.js";
|
||||
import { loadPluginManifestRegistryForPluginRegistry } from "./plugin-registry.js";
|
||||
|
||||
export type ProviderAuthChoiceMetadata = {
|
||||
pluginId: string;
|
||||
@@ -180,12 +180,12 @@ function resolveManifestProviderAuthChoiceCandidates(params?: {
|
||||
env?: NodeJS.ProcessEnv;
|
||||
includeUntrustedWorkspacePlugins?: boolean;
|
||||
}): ProviderAuthChoiceCandidate[] {
|
||||
const registry = loadPluginManifestRegistryForPluginRegistry({
|
||||
config: params?.config,
|
||||
const metadataSnapshot = loadPluginMetadataSnapshot({
|
||||
config: params?.config ?? {},
|
||||
workspaceDir: params?.workspaceDir,
|
||||
env: params?.env,
|
||||
includeDisabled: true,
|
||||
env: params?.env ?? process.env,
|
||||
});
|
||||
const registry = metadataSnapshot.manifestRegistry;
|
||||
const normalizedConfig = normalizePluginsConfig(params?.config?.plugins);
|
||||
return registry.plugins.flatMap((plugin) => {
|
||||
if (
|
||||
|
||||
@@ -3,6 +3,7 @@ import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
const mocks = vi.hoisted(() => ({
|
||||
loadPluginRegistrySnapshot: vi.fn(),
|
||||
loadPluginManifestRegistryForInstalledIndex: vi.fn(),
|
||||
loadPluginMetadataSnapshot: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("./plugin-registry.js", () => ({
|
||||
@@ -19,6 +20,10 @@ vi.mock("./manifest-registry-installed.js", () => ({
|
||||
mocks.loadPluginManifestRegistryForInstalledIndex(...args),
|
||||
}));
|
||||
|
||||
vi.mock("./plugin-metadata-snapshot.js", () => ({
|
||||
loadPluginMetadataSnapshot: (...args: unknown[]) => mocks.loadPluginMetadataSnapshot(...args),
|
||||
}));
|
||||
|
||||
let resolveManifestDeclaredWebProviderCandidatePluginIds: typeof import("./web-provider-resolution-shared.js").resolveManifestDeclaredWebProviderCandidatePluginIds;
|
||||
|
||||
describe("resolveManifestDeclaredWebProviderCandidatePluginIds", () => {
|
||||
@@ -52,6 +57,10 @@ describe("resolveManifestDeclaredWebProviderCandidatePluginIds", () => {
|
||||
],
|
||||
diagnostics: [],
|
||||
});
|
||||
mocks.loadPluginMetadataSnapshot.mockReset();
|
||||
mocks.loadPluginMetadataSnapshot.mockImplementation((...args: unknown[]) => ({
|
||||
plugins: mocks.loadPluginManifestRegistryForInstalledIndex(...args).plugins,
|
||||
}));
|
||||
});
|
||||
|
||||
it("treats explicit empty plugin scopes as scoped-empty", () => {
|
||||
@@ -73,11 +82,7 @@ describe("resolveManifestDeclaredWebProviderCandidatePluginIds", () => {
|
||||
onlyPluginIds: ["missing-plugin"],
|
||||
}),
|
||||
).toEqual([]);
|
||||
expect(mocks.loadPluginManifestRegistryForInstalledIndex).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
pluginIds: ["missing-plugin"],
|
||||
}),
|
||||
);
|
||||
expect(mocks.loadPluginMetadataSnapshot).toHaveBeenCalledOnce();
|
||||
});
|
||||
|
||||
it("keeps origin filters with no declared web candidates scoped-empty", () => {
|
||||
@@ -110,7 +115,7 @@ describe("resolveManifestDeclaredWebProviderCandidatePluginIds", () => {
|
||||
configKey: "webSearch",
|
||||
}),
|
||||
).toEqual(["alpha", "beta"]);
|
||||
expect(mocks.loadPluginRegistrySnapshot).toHaveBeenCalledTimes(1);
|
||||
expect(mocks.loadPluginMetadataSnapshot).toHaveBeenCalledTimes(1);
|
||||
expect(mocks.loadPluginManifestRegistryForInstalledIndex).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { resolveBundledPluginCompatibleLoadValues } from "./activation-context.js";
|
||||
import type { PluginLoadOptions } from "./loader.js";
|
||||
import type { PluginManifestRecord } from "./manifest-registry.js";
|
||||
import { loadPluginManifestRegistryForPluginRegistry } from "./plugin-registry.js";
|
||||
import { loadPluginMetadataSnapshot } from "./plugin-metadata-snapshot.js";
|
||||
import { createPluginIdScopeSet, normalizePluginIdScope } from "./plugin-scope.js";
|
||||
|
||||
export type WebProviderContract = "webSearchProviders" | "webFetchProviders";
|
||||
@@ -66,13 +66,13 @@ function loadInstalledWebProviderManifestRecords(params: {
|
||||
env?: PluginLoadOptions["env"];
|
||||
pluginIds?: readonly string[];
|
||||
}): readonly PluginManifestRecord[] {
|
||||
return loadPluginManifestRegistryForPluginRegistry({
|
||||
config: params.config,
|
||||
const records = loadPluginMetadataSnapshot({
|
||||
config: params.config ?? {},
|
||||
workspaceDir: params.workspaceDir,
|
||||
env: params.env,
|
||||
pluginIds: params.pluginIds,
|
||||
includeDisabled: true,
|
||||
env: params.env ?? process.env,
|
||||
}).plugins;
|
||||
const pluginIdSet = createPluginIdScopeSet(params.pluginIds);
|
||||
return pluginIdSet ? records.filter((plugin) => pluginIdSet.has(plugin.id)) : records;
|
||||
}
|
||||
|
||||
export function resolveManifestDeclaredWebProviderCandidatePluginIds(params: {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { OpenClawConfig } from "../config/types.openclaw.js";
|
||||
import type { PluginManifestRecord } from "./manifest-registry.js";
|
||||
import { loadPluginManifestRegistryForPluginRegistry } from "./plugin-registry.js";
|
||||
import { loadPluginMetadataSnapshot } from "./plugin-metadata-snapshot.js";
|
||||
|
||||
function hasConfiguredCredentialValue(value: unknown): boolean {
|
||||
if (typeof value === "string") {
|
||||
@@ -42,10 +42,9 @@ function hasManifestWebSearchEnvCredentialCandidate(params: {
|
||||
if (!env) {
|
||||
return false;
|
||||
}
|
||||
return loadPluginManifestRegistryForPluginRegistry({
|
||||
return loadPluginMetadataSnapshot({
|
||||
config: params.config,
|
||||
env,
|
||||
includeDisabled: true,
|
||||
}).plugins.some((plugin) => {
|
||||
if (params.origin && plugin.origin !== params.origin) {
|
||||
return false;
|
||||
|
||||
@@ -18,6 +18,10 @@ const pluginRegistryMocks = vi.hoisted(() => {
|
||||
loadPluginManifestRegistryForInstalledIndex: loadManifestRegistry,
|
||||
loadPluginManifestRegistryForPluginRegistry: loadManifestRegistry,
|
||||
loadPluginRegistrySnapshot: vi.fn(() => ({ plugins: [] })),
|
||||
loadPluginMetadataSnapshot: vi.fn(() => ({
|
||||
plugins: loadManifestRegistry().plugins,
|
||||
manifestRegistry: loadManifestRegistry(),
|
||||
})),
|
||||
};
|
||||
});
|
||||
|
||||
@@ -32,6 +36,10 @@ vi.mock("../plugins/plugin-registry.js", () => ({
|
||||
loadPluginRegistrySnapshot: pluginRegistryMocks.loadPluginRegistrySnapshot,
|
||||
}));
|
||||
|
||||
vi.mock("../plugins/plugin-metadata-snapshot.js", () => ({
|
||||
loadPluginMetadataSnapshot: pluginRegistryMocks.loadPluginMetadataSnapshot,
|
||||
}));
|
||||
|
||||
describe("channel env vars dynamic manifest metadata", () => {
|
||||
beforeEach(() => {
|
||||
vi.resetModules();
|
||||
@@ -42,6 +50,7 @@ describe("channel env vars dynamic manifest metadata", () => {
|
||||
});
|
||||
pluginRegistryMocks.loadPluginRegistrySnapshot.mockReset();
|
||||
pluginRegistryMocks.loadPluginRegistrySnapshot.mockReturnValue({ plugins: [] });
|
||||
pluginRegistryMocks.loadPluginMetadataSnapshot.mockClear();
|
||||
});
|
||||
|
||||
it("includes later-installed plugin env vars without a bundled generated map", async () => {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { OpenClawConfig } from "../config/types.openclaw.js";
|
||||
import { loadPluginManifestRegistryForPluginRegistry } from "../plugins/plugin-registry.js";
|
||||
import { loadPluginMetadataSnapshot } from "../plugins/plugin-metadata-snapshot.js";
|
||||
export { isSafeChannelEnvVarTriggerName } from "./channel-env-var-names.js";
|
||||
|
||||
type ChannelEnvVarLookupParams = {
|
||||
@@ -32,14 +32,13 @@ function appendUniqueEnvVarCandidates(
|
||||
export function resolveChannelEnvVars(
|
||||
params?: ChannelEnvVarLookupParams,
|
||||
): Record<string, readonly string[]> {
|
||||
const registry = loadPluginManifestRegistryForPluginRegistry({
|
||||
config: params?.config,
|
||||
const snapshot = loadPluginMetadataSnapshot({
|
||||
config: params?.config ?? {},
|
||||
workspaceDir: params?.workspaceDir,
|
||||
env: params?.env,
|
||||
includeDisabled: true,
|
||||
env: params?.env ?? process.env,
|
||||
});
|
||||
const candidates: Record<string, string[]> = {};
|
||||
for (const plugin of registry.plugins) {
|
||||
for (const plugin of snapshot.plugins) {
|
||||
if (!plugin.channelEnvVars) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { PluginManifestRecord } from "../plugins/manifest-registry.js";
|
||||
import { loadPluginManifestRegistryForPluginRegistry } from "../plugins/plugin-registry.js";
|
||||
import { loadPluginMetadataSnapshot } from "../plugins/plugin-metadata-snapshot.js";
|
||||
import { loadBundledChannelSecretContractApi } from "./channel-contract-api.js";
|
||||
import type { SecretTargetRegistryEntry } from "./target-registry-types.js";
|
||||
|
||||
@@ -47,13 +47,11 @@ function hasWebProviderContract(
|
||||
return (plugin.contracts?.[contract]?.length ?? 0) > 0;
|
||||
}
|
||||
|
||||
function listBundledWebProviderSecretTargetRegistryEntries(): SecretTargetRegistryEntry[] {
|
||||
function listBundledWebProviderSecretTargetRegistryEntries(
|
||||
bundledPlugins: readonly PluginManifestRecord[],
|
||||
): SecretTargetRegistryEntry[] {
|
||||
const entries: SecretTargetRegistryEntry[] = [];
|
||||
for (const record of loadPluginManifestRegistryForPluginRegistry({ includeDisabled: true })
|
||||
.plugins) {
|
||||
if (record.origin !== "bundled") {
|
||||
continue;
|
||||
}
|
||||
for (const record of bundledPlugins) {
|
||||
for (const config of WEB_PROVIDER_SECRET_CONFIGS) {
|
||||
if (
|
||||
hasWebProviderContract(record, config.contract) &&
|
||||
@@ -66,14 +64,12 @@ function listBundledWebProviderSecretTargetRegistryEntries(): SecretTargetRegist
|
||||
return entries.toSorted((left, right) => left.id.localeCompare(right.id));
|
||||
}
|
||||
|
||||
function listBundledPluginConfigSecretTargetRegistryEntries(): SecretTargetRegistryEntry[] {
|
||||
function listBundledPluginConfigSecretTargetRegistryEntries(
|
||||
bundledPlugins: readonly PluginManifestRecord[],
|
||||
): SecretTargetRegistryEntry[] {
|
||||
const entries: SecretTargetRegistryEntry[] = [];
|
||||
const seen = new Set<string>();
|
||||
for (const record of loadPluginManifestRegistryForPluginRegistry({ includeDisabled: true })
|
||||
.plugins) {
|
||||
if (record.origin !== "bundled") {
|
||||
continue;
|
||||
}
|
||||
for (const record of bundledPlugins) {
|
||||
const secretInputs = record.configContracts?.secretInputs?.paths ?? [];
|
||||
for (const secretInput of secretInputs) {
|
||||
const entry = createPluginOpenClawConfigSecretTargetEntry(record.id, secretInput.path);
|
||||
@@ -88,14 +84,12 @@ function listBundledPluginConfigSecretTargetRegistryEntries(): SecretTargetRegis
|
||||
return entries.toSorted((left, right) => left.id.localeCompare(right.id));
|
||||
}
|
||||
|
||||
function listChannelSecretTargetRegistryEntries(): SecretTargetRegistryEntry[] {
|
||||
function listChannelSecretTargetRegistryEntries(
|
||||
bundledPlugins: readonly PluginManifestRecord[],
|
||||
): SecretTargetRegistryEntry[] {
|
||||
const entries: SecretTargetRegistryEntry[] = [];
|
||||
|
||||
for (const record of loadPluginManifestRegistryForPluginRegistry({ includeDisabled: true })
|
||||
.plugins) {
|
||||
if (record.origin !== "bundled") {
|
||||
continue;
|
||||
}
|
||||
for (const record of bundledPlugins) {
|
||||
const channelIds = record.channels;
|
||||
if (channelIds.length === 0) {
|
||||
continue;
|
||||
@@ -455,11 +449,15 @@ export function getSecretTargetRegistry(): SecretTargetRegistryEntry[] {
|
||||
if (cachedSecretTargetRegistry) {
|
||||
return cachedSecretTargetRegistry;
|
||||
}
|
||||
const bundledPlugins = loadPluginMetadataSnapshot({
|
||||
config: {},
|
||||
env: process.env,
|
||||
}).plugins.filter((record) => record.origin === "bundled");
|
||||
cachedSecretTargetRegistry = [
|
||||
...CORE_SECRET_TARGET_REGISTRY,
|
||||
...listBundledWebProviderSecretTargetRegistryEntries(),
|
||||
...listBundledPluginConfigSecretTargetRegistryEntries(),
|
||||
...listChannelSecretTargetRegistryEntries(),
|
||||
...listBundledWebProviderSecretTargetRegistryEntries(bundledPlugins),
|
||||
...listBundledPluginConfigSecretTargetRegistryEntries(bundledPlugins),
|
||||
...listChannelSecretTargetRegistryEntries(bundledPlugins),
|
||||
];
|
||||
return cachedSecretTargetRegistry;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user