fix: scope cold plugin manifests to index

This commit is contained in:
Shakker
2026-04-26 03:39:34 +01:00
parent f8a677bcfd
commit 1a193b2d96
48 changed files with 316 additions and 335 deletions

View File

@@ -30,6 +30,7 @@ Docs: https://docs.openclaw.ai
- Plugins/startup: normalize startup and provider plugin enablement through registry aliases so boot paths do not need the legacy manifest alias scan. Thanks @vincentkoc.
- Providers/plugins: resolve provider ownership, provider discovery scopes, and catalog-hook provider ids from the cold plugin registry instead of rescanning manifests on those paths. Thanks @vincentkoc.
- Plugins/registry: keep installed plugin index records focused on install/state/load paths and resolve plugin capabilities from manifests scoped to indexed plugins. Thanks @shakkernerd.
- Plugins/registry: route cold manifest and capability lookups through the installed plugin index so setup, channels, config, secrets, doctor, and provider metadata paths avoid broad plugin-root scans before runtime execution. Thanks @shakkernerd.
- Plugins/chat commands: refresh the persisted plugin registry after `/plugins enable` and `/plugins disable`, matching the CLI mutation path. Thanks @vincentkoc.
- Plugins/compat: mark `OPENCLAW_DISABLE_PERSISTED_PLUGIN_REGISTRY` as a deprecated break-glass switch and point operators at registry repair instead. Thanks @vincentkoc.
- Plugins/registry: ignore stale persisted registry reads when plugin policy no longer matches current config, and stamp generated registry files with a do-not-edit warning. Thanks @vincentkoc.

View File

@@ -1,6 +1,5 @@
import type { SecretRefSource } from "../config/types.secrets.js";
import { loadPluginManifestRegistryForInstalledIndex } from "../plugins/manifest-registry-installed.js";
import { loadPluginRegistrySnapshot } from "../plugins/plugin-registry.js";
import { loadPluginManifestRegistryForPluginRegistry } from "../plugins/plugin-registry.js";
import { listKnownProviderEnvApiKeyNames } from "./model-auth-env-vars.js";
export const MINIMAX_OAUTH_MARKER = "minimax-oauth";
@@ -45,20 +44,14 @@ function listKnownEnvApiKeyMarkers(): Set<string> {
}
export function listKnownNonSecretApiKeyMarkers(): string[] {
knownNonSecretApiKeyMarkersCache ??= (() => {
const index = loadPluginRegistrySnapshot({});
return [
...new Set([
...CORE_NON_SECRET_API_KEY_MARKERS,
...loadPluginManifestRegistryForInstalledIndex({
index,
includeDisabled: true,
}).plugins.flatMap((plugin) =>
plugin.origin === "bundled" ? (plugin.nonSecretAuthMarkers ?? []) : [],
),
]),
];
})();
knownNonSecretApiKeyMarkersCache ??= [
...new Set([
...CORE_NON_SECRET_API_KEY_MARKERS,
...loadPluginManifestRegistryForPluginRegistry({ includeDisabled: true }).plugins.flatMap(
(plugin) => (plugin.origin === "bundled" ? (plugin.nonSecretAuthMarkers ?? []) : []),
),
]),
];
return [...knownNonSecretApiKeyMarkersCache];
}

View File

@@ -10,9 +10,8 @@ import {
normalizePluginsConfigWithResolver,
resolveEffectivePluginActivationState,
} from "../plugins/config-policy.js";
import { loadPluginManifestRegistryForInstalledIndex } from "../plugins/manifest-registry-installed.js";
import type { PluginManifestRegistry } from "../plugins/manifest-registry.js";
import { loadPluginRegistrySnapshot } from "../plugins/plugin-registry.js";
import { loadPluginManifestRegistryForPluginRegistry } from "../plugins/plugin-registry.js";
import { isRecord } from "../utils.js";
import { loadEmbeddedPiMcpConfig } from "./embedded-pi-mcp.js";
@@ -105,12 +104,7 @@ export function loadEnabledBundlePiSettingsSnapshot(params: {
if (!workspaceDir) {
return {};
}
const index = loadPluginRegistrySnapshot({
workspaceDir,
config: params.cfg,
});
const registry = loadPluginManifestRegistryForInstalledIndex({
index,
const registry = loadPluginManifestRegistryForPluginRegistry({
workspaceDir,
config: params.cfg,
includeDisabled: true,

View File

@@ -45,9 +45,39 @@ vi.mock("../plugins/manifest-registry-installed.js", async () => {
};
});
vi.mock("../plugins/plugin-registry.js", () => ({
loadPluginRegistrySnapshot: () => ({ plugins: [] }),
}));
vi.mock("../plugins/plugin-registry.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 {
loadPluginManifestRegistryForPluginRegistry: loadRegistry,
loadPluginRegistrySnapshot: () => ({ plugins: [] }),
};
});
vi.mock("./embedded-pi-mcp.js", async () => {
const fs = await import("node:fs");

View File

@@ -1,5 +1,4 @@
import { loadPluginManifestRegistryForInstalledIndex } from "../plugins/manifest-registry-installed.js";
import { loadPluginRegistrySnapshot } from "../plugins/plugin-registry.js";
import { loadPluginManifestRegistryForPluginRegistry } from "../plugins/plugin-registry.js";
import {
normalizeOptionalLowercaseString,
normalizeOptionalString,
@@ -209,11 +208,7 @@ function isManifestProviderEndpointClass(value: string): value is ProviderEndpoi
function loadManifestProviderEndpointCache(): ManifestProviderEndpointCacheEntry[] {
if (!manifestProviderEndpointCache) {
const index = loadPluginRegistrySnapshot({ cache: true });
const registry = loadPluginManifestRegistryForInstalledIndex({
index,
includeDisabled: true,
});
const registry = loadPluginManifestRegistryForPluginRegistry({ includeDisabled: true });
const entries: ManifestProviderEndpointCacheEntry[] = [];
for (const plugin of registry.plugins) {
for (const endpoint of plugin.providerEndpoints ?? []) {

View File

@@ -1,9 +1,13 @@
import { describe, expect, it, vi } from "vitest";
const pluginRegistryMocks = vi.hoisted(() => ({
loadPluginManifestRegistryForInstalledIndex: vi.fn(),
loadPluginRegistrySnapshot: vi.fn(() => ({ plugins: [] })),
}));
const pluginRegistryMocks = vi.hoisted(() => {
const loadManifestRegistry = vi.fn();
return {
loadPluginManifestRegistryForInstalledIndex: loadManifestRegistry,
loadPluginManifestRegistryForPluginRegistry: loadManifestRegistry,
loadPluginRegistrySnapshot: vi.fn(() => ({ plugins: [] })),
};
});
vi.mock("../plugins/manifest-registry-installed.js", () => ({
loadPluginManifestRegistryForInstalledIndex:
@@ -11,6 +15,8 @@ vi.mock("../plugins/manifest-registry-installed.js", () => ({
}));
vi.mock("../plugins/plugin-registry.js", () => ({
loadPluginManifestRegistryForPluginRegistry:
pluginRegistryMocks.loadPluginManifestRegistryForPluginRegistry,
loadPluginRegistrySnapshot: pluginRegistryMocks.loadPluginRegistrySnapshot,
}));

View File

@@ -1,12 +1,11 @@
import type { OpenClawConfig } from "../config/types.openclaw.js";
import { loadPluginManifestRegistryForInstalledIndex } from "../plugins/manifest-registry-installed.js";
import type { PluginManifestRecord } from "../plugins/manifest-registry.js";
import {
isWorkspacePluginAllowedByConfig,
normalizePluginConfigId,
} from "../plugins/plugin-config-trust.js";
import type { PluginOrigin } from "../plugins/plugin-origin.types.js";
import { loadPluginRegistrySnapshot } from "../plugins/plugin-registry.js";
import { loadPluginManifestRegistryForPluginRegistry } from "../plugins/plugin-registry.js";
import { normalizeProviderId } from "./provider-id.js";
export type ProviderAuthAliasLookupParams = {
@@ -84,13 +83,7 @@ function setPreferredAlias(params: {
export function resolveProviderAuthAliasMap(
params?: ProviderAuthAliasLookupParams,
): Record<string, string> {
const index = loadPluginRegistrySnapshot({
config: params?.config,
workspaceDir: params?.workspaceDir,
env: params?.env,
});
const registry = loadPluginManifestRegistryForInstalledIndex({
index,
const registry = loadPluginManifestRegistryForPluginRegistry({
config: params?.config,
workspaceDir: params?.workspaceDir,
env: params?.env,

View File

@@ -5,16 +5,21 @@ import type { OpenClawConfig } from "../../config/config.js";
import type { PluginManifestRegistry } from "../../plugins/manifest-registry.js";
import { createTrackedTempDirs } from "../../test-utils/tracked-temp-dirs.js";
const hoisted = vi.hoisted(() => ({
loadPluginManifestRegistryForInstalledIndex: vi.fn(),
loadPluginRegistrySnapshot: vi.fn(() => ({ plugins: [] })),
}));
const hoisted = vi.hoisted(() => {
const loadManifestRegistry = vi.fn();
return {
loadPluginManifestRegistryForInstalledIndex: loadManifestRegistry,
loadPluginManifestRegistryForPluginRegistry: loadManifestRegistry,
loadPluginRegistrySnapshot: vi.fn(() => ({ plugins: [] })),
};
});
vi.mock("../../plugins/manifest-registry-installed.js", () => ({
loadPluginManifestRegistryForInstalledIndex: hoisted.loadPluginManifestRegistryForInstalledIndex,
}));
vi.mock("../../plugins/plugin-registry.js", () => ({
loadPluginManifestRegistryForPluginRegistry: hoisted.loadPluginManifestRegistryForPluginRegistry,
loadPluginRegistrySnapshot: hoisted.loadPluginRegistrySnapshot,
}));

View File

@@ -7,9 +7,8 @@ import {
resolveEffectivePluginActivationState,
resolveMemorySlotDecision,
} from "../../plugins/config-policy.js";
import { loadPluginManifestRegistryForInstalledIndex } from "../../plugins/manifest-registry-installed.js";
import type { PluginManifestRegistry } from "../../plugins/manifest-registry.js";
import { loadPluginRegistrySnapshot } from "../../plugins/plugin-registry.js";
import { loadPluginManifestRegistryForPluginRegistry } from "../../plugins/plugin-registry.js";
import { hasKind } from "../../plugins/slots.js";
import { isPathInsideWithRealpath } from "../../security/scan-paths.js";
@@ -50,12 +49,7 @@ export function resolvePluginSkillDirs(params: {
if (!workspaceDir) {
return [];
}
const index = loadPluginRegistrySnapshot({
workspaceDir,
config: params.config,
});
const registry = loadPluginManifestRegistryForInstalledIndex({
index,
const registry = loadPluginManifestRegistryForPluginRegistry({
workspaceDir,
config: params.config,
includeDisabled: true,

View File

@@ -7,9 +7,8 @@ import {
resolveDiscoverableScopedChannelPluginIds,
} from "../../plugins/channel-plugin-ids.js";
import { loadOpenClawPlugins } from "../../plugins/loader.js";
import { loadPluginManifestRegistryForInstalledIndex } from "../../plugins/manifest-registry-installed.js";
import type { PluginManifestRecord } from "../../plugins/manifest-registry.js";
import { loadPluginRegistrySnapshot } from "../../plugins/plugin-registry.js";
import { loadPluginManifestRegistryForPluginRegistry } from "../../plugins/plugin-registry.js";
import { DEFAULT_ACCOUNT_ID, normalizeAccountId } from "../../routing/session-key.js";
import { sanitizeForLog } from "../../terminal/ansi.js";
import { getBundledChannelSetupPlugin } from "./bundled.js";
@@ -521,17 +520,11 @@ export function resolveReadOnlyChannelPluginsForConfig(
): ReadOnlyChannelPluginResolution {
const env = options.env ?? process.env;
const workspaceDir = resolveReadOnlyWorkspaceDir(cfg, options);
const pluginIndex = loadPluginRegistrySnapshot({
const manifestRecords = loadPluginManifestRegistryForPluginRegistry({
config: cfg,
workspaceDir,
env,
cache: options.cache,
});
const manifestRecords = loadPluginManifestRegistryForInstalledIndex({
index: pluginIndex,
config: cfg,
workspaceDir,
env,
includeDisabled: true,
}).plugins;
const externalManifestRecords = listExternalChannelManifestRecords(manifestRecords);

View File

@@ -4,7 +4,7 @@ import {
normalizePluginsConfig,
resolveEffectivePluginActivationState,
} from "../../../plugins/config-state.js";
import { loadPluginManifestRegistry } from "../../../plugins/manifest-registry.js";
import { loadPluginManifestRegistryForPluginRegistry } from "../../../plugins/plugin-registry.js";
import { sanitizeForLog } from "../../../terminal/ansi.js";
export type ChannelPluginBlockerHit = {
@@ -45,9 +45,10 @@ export function scanConfiguredChannelPluginBlockers(
}
const pluginsConfig = normalizePluginsConfig(cfg.plugins);
const registry = loadPluginManifestRegistry({
const registry = loadPluginManifestRegistryForPluginRegistry({
config: cfg,
env,
includeDisabled: true,
});
const hits: ChannelPluginBlockerHit[] = [];

View File

@@ -1,7 +1,7 @@
import { resolveAgentWorkspaceDir, resolveDefaultAgentId } from "../../../agents/agent-scope.js";
import type { OpenClawConfig } from "../../../config/types.openclaw.js";
import { normalizePluginId } from "../../../plugins/config-state.js";
import { loadPluginManifestRegistry } from "../../../plugins/manifest-registry.js";
import { loadPluginManifestRegistryForPluginRegistry } from "../../../plugins/plugin-registry.js";
import { sanitizeForLog } from "../../../terminal/ansi.js";
import { asObjectRecord } from "./object.js";
@@ -23,10 +23,11 @@ function collectPluginRegistryState(
env?: NodeJS.ProcessEnv,
): StalePluginRegistryState {
const workspaceDir = resolveAgentWorkspaceDir(cfg, resolveDefaultAgentId(cfg));
const registry = loadPluginManifestRegistry({
const registry = loadPluginManifestRegistryForPluginRegistry({
config: cfg,
workspaceDir: workspaceDir ?? undefined,
env,
includeDisabled: true,
});
return {
knownIds: new Set(registry.plugins.map((plugin) => plugin.id)),

View File

@@ -6,11 +6,10 @@ import {
} from "../channels/config-presence.js";
import { getChatChannelMeta, normalizeChatChannelId } from "../channels/registry.js";
import {
loadPluginManifestRegistry,
resolveManifestContractOwnerPluginId,
type PluginManifestRecord,
type PluginManifestRegistry,
} from "../plugins/manifest-registry.js";
import { loadPluginManifestRegistryForPluginRegistry } from "../plugins/plugin-registry.js";
import { resolveOwningPluginIdsForModelRef } from "../plugins/providers.js";
import { resolvePluginSetupAutoEnableReasons } from "../plugins/setup-registry.js";
import { normalizeOptionalLowercaseString } from "../shared/string-coerce.js";
@@ -211,12 +210,20 @@ function resolvePluginIdForConfiguredWebFetchProvider(
providerId: string | undefined,
env: NodeJS.ProcessEnv,
): string | undefined {
return resolveManifestContractOwnerPluginId({
contract: "webFetchProviders",
value: normalizeOptionalLowercaseString(providerId) ?? "",
origin: "bundled",
const normalizedProviderId = normalizeOptionalLowercaseString(providerId);
if (!normalizedProviderId) {
return undefined;
}
return loadPluginManifestRegistryForPluginRegistry({
env,
});
includeDisabled: true,
}).plugins.find(
(plugin) =>
plugin.origin === "bundled" &&
(plugin.contracts?.webFetchProviders ?? []).some(
(candidate) => normalizeOptionalLowercaseString(candidate) === normalizedProviderId,
),
)?.id;
}
function normalizeManifestChannelId(channelId: string): string {
@@ -785,7 +792,11 @@ export function resolvePluginAutoEnableManifestRegistry(params: {
return (
params.manifestRegistry ??
(configMayNeedPluginManifestRegistry(params.config, params.env)
? loadPluginManifestRegistry({ config: params.config, env: params.env })
? loadPluginManifestRegistryForPluginRegistry({
config: params.config,
env: params.env,
includeDisabled: true,
})
: EMPTY_PLUGIN_MANIFEST_REGISTRY)
);
}

View File

@@ -1,5 +1,5 @@
import { resolveAgentWorkspaceDir, resolveDefaultAgentId } from "../agents/agent-scope.js";
import { loadPluginManifestRegistry } from "../plugins/manifest-registry.js";
import { loadPluginManifestRegistryForPluginRegistry } from "../plugins/plugin-registry.js";
import {
collectChannelSchemaMetadata,
collectPluginSchemaMetadata,
@@ -10,11 +10,12 @@ import { buildConfigSchema, type ConfigSchemaResponse } from "./schema.js";
function loadManifestRegistry(config: OpenClawConfig, env?: NodeJS.ProcessEnv) {
const workspaceDir = resolveAgentWorkspaceDir(config, resolveDefaultAgentId(config));
return loadPluginManifestRegistry({
return loadPluginManifestRegistryForPluginRegistry({
config,
cache: false,
env,
workspaceDir,
includeDisabled: true,
});
}

View File

@@ -13,10 +13,8 @@ import {
listPluginDoctorLegacyConfigRules,
} from "../plugins/doctor-contract-registry.js";
import { resolveManifestCommandAliasOwner } from "../plugins/manifest-command-aliases.runtime.js";
import {
loadPluginManifestRegistry,
resolveManifestContractPluginIds,
} from "../plugins/manifest-registry.js";
import type { PluginManifestRegistry } from "../plugins/manifest-registry.js";
import { loadPluginManifestRegistryForPluginRegistry } from "../plugins/plugin-registry.js";
import { validateJsonSchemaValue } from "../plugins/schema-validator.js";
import { hasKind } from "../plugins/slots.js";
import { collectLegacySecretRefEnvMarkerCandidates } from "../secrets/legacy-secretref-env-marker.js";
@@ -760,7 +758,7 @@ function validateConfigObjectWithPluginsBase(
};
type RegistryInfo = {
registry: ReturnType<typeof loadPluginManifestRegistry>;
registry: PluginManifestRegistry;
knownIds?: Set<string>;
overriddenPluginIds?: Set<string>;
normalizedPlugins?: ReturnType<typeof normalizePluginsConfig>;
@@ -788,24 +786,27 @@ function validateConfigObjectWithPluginsBase(
return compatPluginIds;
}
const workspaceDir = resolveAgentWorkspaceDir(config, resolveDefaultAgentId(config));
const registry = loadPluginManifestRegistryForPluginRegistry({
config,
workspaceDir: workspaceDir ?? undefined,
env: opts.env,
includeDisabled: true,
});
const overriddenBundledPluginIds = new Set(
loadPluginManifestRegistry({
config,
workspaceDir: workspaceDir ?? undefined,
env: opts.env,
})
.diagnostics.filter((diag) => diag.message.includes("duplicate plugin id detected"))
registry.diagnostics
.filter((diag) => diag.message.includes("duplicate plugin id detected"))
.map((diag) => diag.pluginId)
.filter((pluginId): pluginId is string => typeof pluginId === "string" && pluginId !== ""),
);
compatPluginIds = new Set(
resolveManifestContractPluginIds({
contract: "webSearchProviders",
origin: "bundled",
config,
workspaceDir: workspaceDir ?? undefined,
env: opts.env,
}).filter((pluginId) => !overriddenBundledPluginIds.has(pluginId)),
registry.plugins
.filter(
(plugin) =>
plugin.origin === "bundled" &&
(plugin.contracts?.webSearchProviders?.length ?? 0) > 0 &&
!overriddenBundledPluginIds.has(plugin.id),
)
.map((plugin) => plugin.id),
);
return compatPluginIds;
};
@@ -838,10 +839,11 @@ function validateConfigObjectWithPluginsBase(
effectiveConfig,
resolveDefaultAgentId(effectiveConfig),
);
const registry = loadPluginManifestRegistry({
const registry = loadPluginManifestRegistryForPluginRegistry({
config: effectiveConfig,
workspaceDir: workspaceDir ?? undefined,
env: opts.env,
includeDisabled: true,
});
for (const diag of registry.diagnostics) {

View File

@@ -7,7 +7,7 @@ import {
resolveEffectivePluginActivationState,
resolveMemorySlotDecision,
} from "../plugins/config-state.js";
import { loadPluginManifestRegistry } from "../plugins/manifest-registry.js";
import { loadPluginManifestRegistryForPluginRegistry } from "../plugins/plugin-registry.js";
import { hasKind } from "../plugins/slots.js";
import { isPathInsideWithRealpath } from "../security/scan-paths.js";
@@ -26,11 +26,12 @@ export function resolvePluginHookDirs(params: {
if (!workspaceDir) {
return [];
}
const registry = loadPluginManifestRegistry({
const registry = loadPluginManifestRegistryForPluginRegistry({
workspaceDir,
config: params.config,
// Hook discovery should reflect freshly written bundle manifests immediately.
cache: false,
includeDisabled: true,
});
if (registry.plugins.length === 0) {
return [];

View File

@@ -17,10 +17,8 @@ import {
isActivatedManifestOwner,
passesManifestOwnerBasePolicy,
} from "../plugins/manifest-owner-policy.js";
import {
loadPluginManifestRegistry,
type PluginManifestRecord,
} from "../plugins/manifest-registry.js";
import type { PluginManifestRecord } from "../plugins/manifest-registry.js";
import { loadPluginManifestRegistryForPluginRegistry } from "../plugins/plugin-registry.js";
import { resolveProviderUsageAuthWithPlugin } from "../plugins/provider-runtime.js";
import { resolveProviderAuthEnvVarCandidates } from "../secrets/provider-env-vars.js";
import { normalizeSecretInput } from "../utils/normalize-secret-input.js";
@@ -183,9 +181,10 @@ function resolveUsageCredentialProviderIds(params: {
const providerIds = new Set(normalizeProviderIds([params.provider]));
const providerIdSet = new Set(providerIds);
try {
const registry = loadPluginManifestRegistry({
const registry = loadPluginManifestRegistryForPluginRegistry({
config: params.state.cfg,
env: params.state.env,
includeDisabled: true,
});
for (const plugin of registry.plugins) {
const pluginProviderIds = normalizeProviderIds(plugin.providers);

View File

@@ -1,5 +1,5 @@
import type { OpenClawConfig } from "../config/types.js";
import { loadPluginManifestRegistry } from "../plugins/manifest-registry.js";
import { loadPluginManifestRegistryForPluginRegistry } from "../plugins/plugin-registry.js";
import { normalizeMediaProviderId } from "./provider-id.js";
import type { MediaUnderstandingProvider } from "./types.js";
@@ -7,9 +7,10 @@ export function buildMediaUnderstandingManifestMetadataRegistry(
cfg?: OpenClawConfig,
): Map<string, MediaUnderstandingProvider> {
const registry = new Map<string, MediaUnderstandingProvider>();
for (const plugin of loadPluginManifestRegistry({
for (const plugin of loadPluginManifestRegistryForPluginRegistry({
config: cfg,
env: process.env,
includeDisabled: true,
}).plugins) {
const declaredProviders = new Set(
(plugin.contracts?.mediaUnderstandingProviders ?? []).map((providerId) =>

View File

@@ -8,6 +8,11 @@ vi.mock("./manifest-registry.js", () => ({
loadPluginManifestRegistry: (...args: unknown[]) => mocks.loadPluginManifestRegistry(...args),
}));
vi.mock("./plugin-registry.js", () => ({
loadPluginManifestRegistryForPluginRegistry: (...args: unknown[]) =>
mocks.loadPluginManifestRegistry(...args),
}));
let resolveManifestActivationPluginIds: typeof import("./activation-planner.js").resolveManifestActivationPluginIds;
let resolveManifestActivationPlan: typeof import("./activation-planner.js").resolveManifestActivationPlan;

View File

@@ -1,10 +1,11 @@
import { normalizeProviderId } from "../agents/provider-id.js";
import type { OpenClawConfig } from "../config/types.js";
import { normalizeOptionalLowercaseString } from "../shared/string-coerce.js";
import { loadPluginManifestRegistry, type PluginManifestRecord } from "./manifest-registry.js";
import type { PluginManifestRecord } from "./manifest-registry.js";
import type { PluginDiagnostic } from "./manifest-types.js";
import type { PluginManifestActivationCapability } from "./manifest.js";
import type { PluginOrigin } from "./plugin-origin.types.js";
import { loadPluginManifestRegistryForPluginRegistry } from "./plugin-registry.js";
import { createPluginIdScopeSet, normalizePluginIdScope } from "./plugin-scope.js";
export type PluginActivationPlannerTrigger =
@@ -65,11 +66,12 @@ export function resolveManifestActivationPlan(
const onlyPluginIdSet = createPluginIdScopeSet(normalizePluginIdScope(params.onlyPluginIds));
const registry = params.manifestRecords
? { plugins: params.manifestRecords, diagnostics: [] }
: loadPluginManifestRegistry({
: loadPluginManifestRegistryForPluginRegistry({
config: params.config,
workspaceDir: params.workspaceDir,
env: params.env,
cache: params.cache,
includeDisabled: true,
});
const entries = registry.plugins
.flatMap((plugin) => {

View File

@@ -18,7 +18,7 @@ import {
normalizePluginsConfig,
resolveEffectivePluginActivationState,
} from "./config-state.js";
import { loadPluginManifestRegistry } from "./manifest-registry.js";
import { loadPluginManifestRegistryForPluginRegistry } from "./plugin-registry.js";
export type ClaudeBundleCommandSpec = {
pluginId: string;
@@ -176,10 +176,11 @@ export function loadEnabledClaudeBundleCommands(params: {
if (!hasExplicitPluginConfig(params.cfg?.plugins)) {
return [];
}
const registry = loadPluginManifestRegistry({
const registry = loadPluginManifestRegistryForPluginRegistry({
workspaceDir: params.workspaceDir,
config: params.cfg,
cache: false,
includeDisabled: true,
});
const normalizedPlugins = normalizePluginsConfig(params.cfg?.plugins);
const commands: ClaudeBundleCommandSpec[] = [];

View File

@@ -5,8 +5,8 @@ import type { OpenClawConfig } from "../config/types.openclaw.js";
import { matchBoundaryFileOpenFailure, openBoundaryFileSync } from "../infra/boundary-file-read.js";
import { isRecord } from "../utils.js";
import { normalizePluginsConfig, resolveEffectivePluginActivationState } from "./config-state.js";
import { loadPluginManifestRegistry } from "./manifest-registry.js";
import type { PluginBundleFormat } from "./manifest-types.js";
import { loadPluginManifestRegistryForPluginRegistry } from "./plugin-registry.js";
type ReadBundleJsonResult =
| { ok: true; raw: Record<string, unknown> }
@@ -107,9 +107,10 @@ export function loadEnabledBundleConfig<TConfig, TDiagnostic>(params: {
return { config: params.createEmptyConfig(), diagnostics: [] };
}
const registry = loadPluginManifestRegistry({
const registry = loadPluginManifestRegistryForPluginRegistry({
workspaceDir: params.workspaceDir,
config: params.cfg,
includeDisabled: true,
});
const diagnostics: TDiagnostic[] = [];
let merged = params.createEmptyConfig();

View File

@@ -15,10 +15,10 @@ const mocks = vi.hoisted(() => ({
resolveRuntimePluginRegistry: vi.fn<
(params?: unknown) => ReturnType<typeof createEmptyPluginRegistry> | undefined
>(() => undefined),
loadPluginRegistrySnapshot: vi.fn(() => ({ plugins: [] })),
loadPluginManifestRegistry: vi.fn<() => MockManifestRegistry>(() =>
createEmptyMockManifestRegistry(),
loadPluginManifestRegistry: vi.fn<(params?: Record<string, unknown>) => MockManifestRegistry>(
() => createEmptyMockManifestRegistry(),
),
loadPluginRegistrySnapshot: vi.fn(() => ({ plugins: [] })),
withBundledPluginAllowlistCompat: vi.fn(
({ config, pluginIds }: { config?: OpenClawConfig; pluginIds: string[] }) =>
({
@@ -41,9 +41,21 @@ vi.mock("./manifest-registry-installed.js", () => ({
loadPluginManifestRegistryForInstalledIndex: mocks.loadPluginManifestRegistry,
}));
vi.mock("./plugin-registry.js", () => ({
loadPluginRegistrySnapshot: mocks.loadPluginRegistrySnapshot,
}));
vi.mock("./plugin-registry.js", async (importOriginal) => {
const actual = await importOriginal<typeof import("./plugin-registry.js")>();
return {
...actual,
loadPluginRegistrySnapshot: mocks.loadPluginRegistrySnapshot,
loadPluginManifestRegistryForPluginRegistry: (
...args: Parameters<typeof mocks.loadPluginManifestRegistry>
) => {
const [{ includeDisabled: _includeDisabled, ...params } = {}] = args as [
Record<string, unknown>?,
];
return mocks.loadPluginManifestRegistry(params);
},
};
});
vi.mock("./bundled-compat.js", () => ({
withBundledPluginAllowlistCompat: mocks.withBundledPluginAllowlistCompat,
@@ -73,10 +85,8 @@ function expectBundledCompatLoadPath(params: {
};
}) {
expect(mocks.loadPluginManifestRegistry).toHaveBeenCalledWith({
index: expect.anything(),
config: params.cfg,
env: process.env,
includeDisabled: true,
});
expect(mocks.withBundledPluginEnablementCompat).toHaveBeenCalledWith({
config: params.allowlistCompat,

View File

@@ -6,8 +6,7 @@ import {
} from "./bundled-compat.js";
import { hasExplicitPluginConfig } from "./config-policy.js";
import { resolveRuntimePluginRegistry } from "./loader.js";
import { loadPluginManifestRegistryForInstalledIndex } from "./manifest-registry-installed.js";
import { loadPluginRegistrySnapshot } from "./plugin-registry.js";
import { loadPluginManifestRegistryForPluginRegistry } from "./plugin-registry.js";
import type { PluginRegistry } from "./registry-types.js";
type CapabilityProviderRegistryKey =
@@ -50,12 +49,7 @@ function resolveBundledCapabilityCompatPluginIds(params: {
providerId?: string;
}): string[] {
const contractKey = CAPABILITY_CONTRACT_KEY[params.key];
const index = loadPluginRegistrySnapshot({
config: params.cfg,
env: process.env,
});
return loadPluginManifestRegistryForInstalledIndex({
index,
return loadPluginManifestRegistryForPluginRegistry({
config: params.cfg,
env: process.env,
includeDisabled: true,

View File

@@ -19,9 +19,8 @@ import {
isBundledManifestOwner,
passesManifestOwnerBasePolicy,
} from "./manifest-owner-policy.js";
import { loadPluginManifestRegistryForInstalledIndex } from "./manifest-registry-installed.js";
import type { PluginManifestRecord } from "./manifest-registry.js";
import { loadPluginRegistrySnapshot } from "./plugin-registry.js";
import { loadPluginManifestRegistryForPluginRegistry } from "./plugin-registry.js";
const IGNORED_CHANNEL_CONFIG_KEYS = new Set(["defaults", "modelByChannel"]);
@@ -348,17 +347,11 @@ function loadInstalledChannelManifestRecords(params: {
env: NodeJS.ProcessEnv;
cache?: boolean;
}): readonly PluginManifestRecord[] {
const index = loadPluginRegistrySnapshot({
return loadPluginManifestRegistryForPluginRegistry({
config: params.config,
workspaceDir: params.workspaceDir,
env: params.env,
cache: params.cache,
});
return loadPluginManifestRegistryForInstalledIndex({
index,
config: params.config,
workspaceDir: params.workspaceDir,
env: params.env,
includeDisabled: true,
}).plugins;
}

View File

@@ -1,11 +1,15 @@
import { beforeEach, describe, expect, it, vi } from "vitest";
import type { PluginManifestRegistry } from "./manifest-registry.js";
const mocks = vi.hoisted(() => ({
findBundledPluginMetadataById: vi.fn(),
loadPluginManifestRegistryForInstalledIndex: vi.fn(),
loadPluginRegistrySnapshot: vi.fn(() => ({ plugins: [] })),
}));
const mocks = vi.hoisted(() => {
const loadManifestRegistry = vi.fn();
return {
findBundledPluginMetadataById: vi.fn(),
loadPluginManifestRegistryForInstalledIndex: loadManifestRegistry,
loadPluginManifestRegistryForPluginRegistry: loadManifestRegistry,
loadPluginRegistrySnapshot: vi.fn(() => ({ plugins: [] })),
};
});
vi.mock("./bundled-plugin-metadata.js", () => ({
findBundledPluginMetadataById: mocks.findBundledPluginMetadataById,
@@ -16,6 +20,7 @@ vi.mock("./manifest-registry-installed.js", () => ({
}));
vi.mock("./plugin-registry.js", () => ({
loadPluginManifestRegistryForPluginRegistry: mocks.loadPluginManifestRegistryForPluginRegistry,
loadPluginRegistrySnapshot: mocks.loadPluginRegistrySnapshot,
}));

View File

@@ -1,10 +1,9 @@
import type { OpenClawConfig } from "../config/types.openclaw.js";
import { isRecord } from "../utils.js";
import { findBundledPluginMetadataById } from "./bundled-plugin-metadata.js";
import { loadPluginManifestRegistryForInstalledIndex } from "./manifest-registry-installed.js";
import type { PluginManifestConfigContracts } from "./manifest.js";
import type { PluginOrigin } from "./plugin-origin.types.js";
import { loadPluginRegistrySnapshot } from "./plugin-registry.js";
import { loadPluginManifestRegistryForPluginRegistry } from "./plugin-registry.js";
export type PluginConfigContractMatch = {
path: string;
@@ -115,17 +114,11 @@ export function resolvePluginConfigContractsById(params: {
}
const resolvedPluginIds = new Set<string>();
const index = loadPluginRegistrySnapshot({
const registry = loadPluginManifestRegistryForPluginRegistry({
config: params.config,
workspaceDir: params.workspaceDir,
env: params.env,
cache: params.cache,
});
const registry = loadPluginManifestRegistryForInstalledIndex({
index,
config: params.config,
workspaceDir: params.workspaceDir,
env: params.env,
includeDisabled: true,
});
for (const plugin of registry.plugins) {

View File

@@ -4,10 +4,10 @@ import { fileURLToPath } from "node:url";
import type { LegacyConfigRule } from "../config/legacy.shared.js";
import type { OpenClawConfig } from "../config/types.js";
import { asNullableRecord } from "../shared/record-coerce.js";
import { discoverOpenClawPlugins } from "./discovery.js";
import { getCachedPluginJitiLoader, type PluginJitiLoaderCache } from "./jiti-loader-cache.js";
import { loadPluginManifestRegistry } from "./manifest-registry.js";
import type { PluginManifestRegistry } from "./manifest-registry.js";
import { tryNativeRequireJavaScriptModule } from "./native-module-require.js";
import { loadPluginManifestRegistryForPluginRegistry } from "./plugin-registry.js";
import { resolvePluginCacheInputs, type PluginSourceRoots } from "./roots.js";
const CONTRACT_API_EXTENSIONS = [".js", ".mjs", ".cjs", ".ts", ".mts", ".cts"] as const;
@@ -36,9 +36,7 @@ type PluginDoctorContractEntry = {
normalizeCompatibilityConfig?: PluginDoctorCompatibilityNormalizer;
};
type PluginManifestRegistryRecord = ReturnType<
typeof loadPluginManifestRegistry
>["plugins"][number];
type PluginManifestRegistryRecord = PluginManifestRegistry["plugins"][number];
const jitiLoaders: PluginJitiLoaderCache = new Map();
const doctorContractCache = new Map<string, PluginDoctorContractEntry[]>();
@@ -285,17 +283,11 @@ function resolvePluginDoctorContracts(params?: {
return [];
}
const discovery = discoverOpenClawPlugins({
const manifestRegistry = loadPluginManifestRegistryForPluginRegistry({
workspaceDir: params?.workspaceDir,
env,
cache: true,
});
const manifestRegistry = loadPluginManifestRegistry({
workspaceDir: params?.workspaceDir,
env,
cache: true,
candidates: discovery.candidates,
diagnostics: discovery.diagnostics,
includeDisabled: true,
});
const entries: PluginDoctorContractEntry[] = [];

View File

@@ -7,9 +7,8 @@ import {
} from "./config-state.js";
import { loadBundledDocumentExtractorEntriesFromDir } from "./document-extractor-public-artifacts.js";
import type { PluginDocumentExtractorEntry } from "./document-extractor-types.js";
import { loadPluginManifestRegistryForInstalledIndex } from "./manifest-registry-installed.js";
import type { PluginManifestRecord } from "./manifest-registry.js";
import { loadPluginRegistrySnapshot } from "./plugin-registry.js";
import { loadPluginManifestRegistryForPluginRegistry } from "./plugin-registry.js";
function compareExtractors(
left: PluginDocumentExtractorEntry,
@@ -31,13 +30,7 @@ function resolveBundledDocumentExtractorCompatPluginIds(params: {
}): string[] {
const onlyPluginIdSet =
params.onlyPluginIds && params.onlyPluginIds.length > 0 ? new Set(params.onlyPluginIds) : null;
const index = loadPluginRegistrySnapshot({
config: params.config,
workspaceDir: params.workspaceDir,
env: params.env,
});
return loadPluginManifestRegistryForInstalledIndex({
index,
return loadPluginManifestRegistryForPluginRegistry({
config: params.config,
workspaceDir: params.workspaceDir,
env: params.env,
@@ -82,13 +75,7 @@ function resolveEnabledBundledDocumentExtractorPlugins(params: {
});
const onlyPluginIdSet =
params.onlyPluginIds && params.onlyPluginIds.length > 0 ? new Set(params.onlyPluginIds) : null;
const index = loadPluginRegistrySnapshot({
config: activation.config,
workspaceDir: params.workspaceDir,
env: params.env,
});
return loadPluginManifestRegistryForInstalledIndex({
index,
return loadPluginManifestRegistryForPluginRegistry({
config: activation.config,
workspaceDir: params.workspaceDir,
env: params.env,

View File

@@ -4,8 +4,7 @@ import {
type PluginManifestCommandAliasRegistry,
type PluginManifestCommandAliasRecord,
} from "./manifest-command-aliases.js";
import { loadPluginManifestRegistryForInstalledIndex } from "./manifest-registry-installed.js";
import { loadPluginRegistrySnapshot } from "./plugin-registry.js";
import { loadPluginManifestRegistryForPluginRegistry } from "./plugin-registry.js";
export function resolveManifestCommandAliasOwner(params: {
command: string | undefined;
@@ -16,20 +15,12 @@ export function resolveManifestCommandAliasOwner(params: {
}): PluginManifestCommandAliasRecord | undefined {
const registry =
params.registry ??
(() => {
const index = loadPluginRegistrySnapshot({
config: params.config,
workspaceDir: params.workspaceDir,
env: params.env,
});
return loadPluginManifestRegistryForInstalledIndex({
index,
config: params.config,
workspaceDir: params.workspaceDir,
env: params.env,
includeDisabled: true,
});
})();
loadPluginManifestRegistryForPluginRegistry({
config: params.config,
workspaceDir: params.workspaceDir,
env: params.env,
includeDisabled: true,
});
return resolveManifestCommandAliasOwnerInRegistry({
command: params.command,
registry,

View File

@@ -5,7 +5,9 @@ import {
type NormalizedPluginsConfig,
} from "./config-normalization-shared.js";
import {
inspectPersistedInstalledPluginIndex,
readPersistedInstalledPluginIndexSync,
refreshPersistedInstalledPluginIndex,
type InstalledPluginIndexStoreInspection,
type InstalledPluginIndexStoreOptions,
} from "./installed-plugin-index-store.js";
@@ -61,6 +63,11 @@ export type PluginRegistryContributionOptions = LoadPluginRegistryParams & {
includeDisabled?: boolean;
};
export type LoadPluginRegistryManifestParams = LoadPluginRegistryParams & {
includeDisabled?: boolean;
pluginIds?: readonly string[];
};
export type GetPluginRecordParams = LoadPluginRegistryParams & {
pluginId: string;
};
@@ -190,6 +197,20 @@ function loadContributionManifestRegistry(
});
}
export function loadPluginManifestRegistryForPluginRegistry(
params: LoadPluginRegistryManifestParams = {},
): PluginManifestRegistry {
const index = resolveSnapshot(params);
return loadPluginManifestRegistryForInstalledIndex({
index,
config: params.config,
workspaceDir: params.workspaceDir,
env: params.env,
pluginIds: params.pluginIds,
includeDisabled: params.includeDisabled,
});
}
export function createPluginRegistryIdNormalizer(
index: PluginRegistrySnapshot,
): (pluginId: string) => string {
@@ -422,15 +443,11 @@ export function resolveSetupProviderOwners(
export function inspectPluginRegistry(
params: LoadInstalledPluginIndexParams & InstalledPluginIndexStoreOptions = {},
): Promise<PluginRegistryInspection> {
return import("./installed-plugin-index-store.js").then((store) =>
store.inspectPersistedInstalledPluginIndex(params),
);
return inspectPersistedInstalledPluginIndex(params);
}
export function refreshPluginRegistry(
params: RefreshInstalledPluginIndexParams & InstalledPluginIndexStoreOptions,
): Promise<PluginRegistrySnapshot> {
return import("./installed-plugin-index-store.js").then((store) =>
store.refreshPersistedInstalledPluginIndex(params),
);
return refreshPersistedInstalledPluginIndex(params);
}

View File

@@ -2,6 +2,7 @@ import { beforeEach, describe, expect, it, vi } from "vitest";
const pluginRegistryMocks = vi.hoisted(() => ({
loadPluginManifestRegistryForInstalledIndex: vi.fn(),
loadPluginManifestRegistryForPluginRegistry: vi.fn(),
loadPluginRegistrySnapshot: vi.fn(() => ({ plugins: [] })),
}));
@@ -11,6 +12,8 @@ vi.mock("./manifest-registry-installed.js", () => ({
}));
vi.mock("./plugin-registry.js", () => ({
loadPluginManifestRegistryForPluginRegistry:
pluginRegistryMocks.loadPluginManifestRegistryForPluginRegistry,
loadPluginRegistrySnapshot: pluginRegistryMocks.loadPluginRegistrySnapshot,
}));
@@ -37,6 +40,9 @@ function setManifestPlugins(plugins: Array<Record<string, unknown>>) {
pluginRegistryMocks.loadPluginManifestRegistryForInstalledIndex.mockReturnValue({
plugins,
});
pluginRegistryMocks.loadPluginManifestRegistryForPluginRegistry.mockReturnValue({
plugins,
});
}
function expectResolvedProviderAuthChoices(params: {
@@ -66,6 +72,10 @@ describe("provider auth choice manifest helpers", () => {
pluginRegistryMocks.loadPluginManifestRegistryForInstalledIndex.mockReturnValue({
plugins: [],
});
pluginRegistryMocks.loadPluginManifestRegistryForPluginRegistry.mockReset();
pluginRegistryMocks.loadPluginManifestRegistryForPluginRegistry.mockReturnValue({
plugins: [],
});
pluginRegistryMocks.loadPluginRegistrySnapshot.mockReset();
pluginRegistryMocks.loadPluginRegistrySnapshot.mockReturnValue({ plugins: [] });
});

View File

@@ -2,10 +2,9 @@ import { resolveProviderIdForAuth } from "../agents/provider-auth-aliases.js";
import type { OpenClawConfig } from "../config/types.openclaw.js";
import { sanitizeForLog } from "../terminal/ansi.js";
import { normalizePluginsConfig, resolveEffectiveEnableState } from "./config-state.js";
import { loadPluginManifestRegistryForInstalledIndex } from "./manifest-registry-installed.js";
import type { PluginManifestRecord } from "./manifest-registry.js";
import type { PluginOrigin } from "./plugin-origin.types.js";
import { loadPluginRegistrySnapshot } from "./plugin-registry.js";
import { loadPluginManifestRegistryForPluginRegistry } from "./plugin-registry.js";
export type ProviderAuthChoiceMetadata = {
pluginId: string;
@@ -181,13 +180,7 @@ function resolveManifestProviderAuthChoiceCandidates(params?: {
env?: NodeJS.ProcessEnv;
includeUntrustedWorkspacePlugins?: boolean;
}): ProviderAuthChoiceCandidate[] {
const index = loadPluginRegistrySnapshot({
config: params?.config,
workspaceDir: params?.workspaceDir,
env: params?.env,
});
const registry = loadPluginManifestRegistryForInstalledIndex({
index,
const registry = loadPluginManifestRegistryForPluginRegistry({
config: params?.config,
workspaceDir: params?.workspaceDir,
env: params?.env,

View File

@@ -1,5 +1,6 @@
import type { OpenClawConfig } from "../config/types.openclaw.js";
import { loadPluginManifestRegistry, type PluginManifestRecord } from "./manifest-registry.js";
import type { PluginManifestRecord } from "./manifest-registry.js";
import { loadPluginManifestRegistryForPluginRegistry } from "./plugin-registry.js";
import { resolveDiscoveredProviderPluginIds } from "./providers.js";
import { resolvePluginProviders } from "./providers.runtime.js";
import { createPluginSourceLoader } from "./source-loader.js";
@@ -77,9 +78,11 @@ function resolveProviderDiscoveryEntryPlugins(params: {
}): ProviderDiscoveryEntryResult {
const pluginIds = resolveDiscoveredProviderPluginIds(params);
const pluginIdSet = new Set(pluginIds);
const pluginRecords = loadPluginManifestRegistry(params).plugins.filter((plugin) =>
pluginIdSet.has(plugin.id),
);
const pluginRecords = loadPluginManifestRegistryForPluginRegistry({
...params,
pluginIds,
includeDisabled: true,
}).plugins.filter((plugin) => pluginIdSet.has(plugin.id));
const entryRecords = pluginRecords.filter((plugin) => plugin.providerDiscoverySource);
const entryPluginIds = new Set(entryRecords.map((plugin) => plugin.id));
if (entryRecords.length === 0) {

View File

@@ -6,9 +6,8 @@ import type { OpenClawConfig } from "../config/types.openclaw.js";
import { buildPluginApi } from "./api-builder.js";
import { collectPluginConfigContractMatches } from "./config-contracts.js";
import { getCachedPluginJitiLoader, type PluginJitiLoaderCache } from "./jiti-loader-cache.js";
import { loadPluginManifestRegistryForInstalledIndex } from "./manifest-registry-installed.js";
import type { PluginManifestRecord } from "./manifest-registry.js";
import { loadPluginRegistrySnapshot } from "./plugin-registry.js";
import { loadPluginManifestRegistryForPluginRegistry } from "./plugin-registry.js";
import { resolvePluginCacheInputs } from "./roots.js";
import type { PluginRuntime } from "./runtime/types.js";
import { listSetupCliBackendIds, listSetupProviderIds } from "./setup-descriptors.js";
@@ -252,6 +251,7 @@ function resolveRelevantSetupMigrationPluginIds(params: {
}): string[] {
const ids = new Set<string>(collectConfiguredPluginEntryIds(params.config));
const registry = loadSetupManifestRegistry({
config: params.config,
workspaceDir: params.workspaceDir,
env: params.env,
});
@@ -376,15 +376,14 @@ function matchesProvider(provider: ProviderPlugin, providerId: string): boolean
);
}
function loadSetupManifestRegistry(params?: { workspaceDir?: string; env?: NodeJS.ProcessEnv }) {
function loadSetupManifestRegistry(params?: {
config?: OpenClawConfig;
workspaceDir?: string;
env?: NodeJS.ProcessEnv;
}) {
const env = params?.env ?? process.env;
const index = loadPluginRegistrySnapshot({
workspaceDir: params?.workspaceDir,
env,
cache: true,
});
return loadPluginManifestRegistryForInstalledIndex({
index,
return loadPluginManifestRegistryForPluginRegistry({
config: params?.config,
workspaceDir: params?.workspaceDir,
env,
includeDisabled: true,

View File

@@ -1,6 +1,5 @@
import { normalizeProviderId } from "../agents/provider-id.js";
import { loadPluginManifestRegistryForInstalledIndex } from "./manifest-registry-installed.js";
import { loadPluginRegistrySnapshot } from "./plugin-registry.js";
import { loadPluginManifestRegistryForPluginRegistry } from "./plugin-registry.js";
import { getPluginRegistryState } from "./runtime-state.js";
function uniqueProviderRefs(values: readonly string[]): string[] {
@@ -19,12 +18,10 @@ function uniqueProviderRefs(values: readonly string[]): string[] {
}
function resolveManifestSyntheticAuthProviderRefs(): string[] {
const index = loadPluginRegistrySnapshot({ cache: true });
return uniqueProviderRefs(
loadPluginManifestRegistryForInstalledIndex({
index,
includeDisabled: true,
}).plugins.flatMap((plugin) => plugin.syntheticAuthRefs ?? []),
loadPluginManifestRegistryForPluginRegistry({ includeDisabled: true }).plugins.flatMap(
(plugin) => plugin.syntheticAuthRefs ?? [],
),
);
}

View File

@@ -30,12 +30,18 @@ vi.mock("../manifest-registry-installed.js", () => ({
) => registryJitiMocks.loadPluginManifestRegistry(...args),
}));
vi.mock("../plugin-registry.js", () => ({
loadPluginRegistrySnapshot: (
...args: Parameters<typeof registryJitiMocks.loadPluginRegistrySnapshot>
) => registryJitiMocks.loadPluginRegistrySnapshot(...args),
}));
vi.mock("../plugin-registry.js", async (importOriginal) => {
const actual = await importOriginal<typeof import("../plugin-registry.js")>();
return {
...actual,
loadPluginRegistrySnapshot: (
...args: Parameters<typeof registryJitiMocks.loadPluginRegistrySnapshot>
) => registryJitiMocks.loadPluginRegistrySnapshot(...args),
loadPluginManifestRegistryForPluginRegistry: (
...args: Parameters<typeof registryJitiMocks.loadPluginManifestRegistry>
) => registryJitiMocks.loadPluginManifestRegistry(...args),
};
});
export function resetRegistryJitiMocks(): void {
registryJitiMocks.createJiti.mockReset();
registryJitiMocks.discoverOpenClawPlugins.mockReset();

View File

@@ -5,9 +5,8 @@ import {
normalizePluginsConfig,
resolveEffectivePluginActivationState,
} from "./config-state.js";
import { loadPluginManifestRegistryForInstalledIndex } from "./manifest-registry-installed.js";
import type { PluginManifestRecord } from "./manifest-registry.js";
import { loadPluginRegistrySnapshot } from "./plugin-registry.js";
import { loadPluginManifestRegistryForPluginRegistry } from "./plugin-registry.js";
import { loadBundledWebContentExtractorEntriesFromDir } from "./web-content-extractor-public-artifacts.js";
import type { PluginWebContentExtractorEntry } from "./web-content-extractor-types.js";
@@ -31,13 +30,7 @@ function resolveBundledWebContentExtractorCompatPluginIds(params: {
}): string[] {
const onlyPluginIdSet =
params.onlyPluginIds && params.onlyPluginIds.length > 0 ? new Set(params.onlyPluginIds) : null;
const index = loadPluginRegistrySnapshot({
config: params.config,
workspaceDir: params.workspaceDir,
env: params.env,
});
return loadPluginManifestRegistryForInstalledIndex({
index,
return loadPluginManifestRegistryForPluginRegistry({
config: params.config,
workspaceDir: params.workspaceDir,
env: params.env,
@@ -82,13 +75,7 @@ function resolveEnabledBundledExtractorPlugins(params: {
});
const onlyPluginIdSet =
params.onlyPluginIds && params.onlyPluginIds.length > 0 ? new Set(params.onlyPluginIds) : null;
const index = loadPluginRegistrySnapshot({
config: activation.config,
workspaceDir: params.workspaceDir,
env: params.env,
});
return loadPluginManifestRegistryForInstalledIndex({
index,
return loadPluginManifestRegistryForPluginRegistry({
config: activation.config,
workspaceDir: params.workspaceDir,
env: params.env,

View File

@@ -1,7 +1,6 @@
import path from "node:path";
import type { PluginLoadOptions } from "./loader.js";
import { loadPluginManifestRegistryForInstalledIndex } from "./manifest-registry-installed.js";
import { loadPluginRegistrySnapshot } from "./plugin-registry.js";
import { loadPluginManifestRegistryForPluginRegistry } from "./plugin-registry.js";
import type { PluginWebFetchProviderEntry, PluginWebSearchProviderEntry } from "./types.js";
import { resolveBundledWebFetchResolutionConfig } from "./web-fetch-providers.shared.js";
import {
@@ -57,14 +56,8 @@ function resolveBundledManifestRecordsByPluginId(params: {
onlyPluginIds: readonly string[];
}) {
const allowedPluginIds = new Set(params.onlyPluginIds);
const index = loadPluginRegistrySnapshot({
config: params.config,
workspaceDir: params.workspaceDir,
env: params.env,
});
return new Map(
loadPluginManifestRegistryForInstalledIndex({
index,
loadPluginManifestRegistryForPluginRegistry({
config: params.config,
workspaceDir: params.workspaceDir,
env: params.env,

View File

@@ -1,8 +1,7 @@
import { resolveBundledPluginCompatibleLoadValues } from "./activation-context.js";
import type { PluginLoadOptions } from "./loader.js";
import { loadPluginManifestRegistryForInstalledIndex } from "./manifest-registry-installed.js";
import type { PluginManifestRecord } from "./manifest-registry.js";
import { loadPluginRegistrySnapshot } from "./plugin-registry.js";
import { loadPluginManifestRegistryForPluginRegistry } from "./plugin-registry.js";
import {
createPluginIdScopeSet,
normalizePluginIdScope,
@@ -65,13 +64,7 @@ function loadInstalledWebProviderManifestRecords(params: {
workspaceDir?: string;
env?: PluginLoadOptions["env"];
}): readonly PluginManifestRecord[] {
const index = loadPluginRegistrySnapshot({
config: params.config,
workspaceDir: params.workspaceDir,
env: params.env,
});
return loadPluginManifestRegistryForInstalledIndex({
index,
return loadPluginManifestRegistryForPluginRegistry({
config: params.config,
workspaceDir: params.workspaceDir,
env: params.env,

View File

@@ -1,7 +1,6 @@
import type { OpenClawConfig } from "../config/types.openclaw.js";
import { loadPluginManifestRegistryForInstalledIndex } from "./manifest-registry-installed.js";
import type { PluginManifestRecord } from "./manifest-registry.js";
import { loadPluginRegistrySnapshot } from "./plugin-registry.js";
import { loadPluginManifestRegistryForPluginRegistry } from "./plugin-registry.js";
import { resolvePluginWebSearchProviders } from "./web-search-providers.runtime.js";
function hasConfiguredCredentialValue(value: unknown): boolean {
@@ -44,12 +43,7 @@ function hasManifestWebSearchEnvCredentialCandidate(params: {
if (!env) {
return false;
}
const index = loadPluginRegistrySnapshot({
config: params.config,
env,
});
return loadPluginManifestRegistryForInstalledIndex({
index,
return loadPluginManifestRegistryForPluginRegistry({
config: params.config,
env,
includeDisabled: true,

View File

@@ -9,13 +9,17 @@ type MockManifestRegistry = {
diagnostics: unknown[];
};
const pluginRegistryMocks = vi.hoisted(() => ({
loadPluginManifestRegistryForInstalledIndex: vi.fn<() => MockManifestRegistry>(() => ({
const pluginRegistryMocks = vi.hoisted(() => {
const loadManifestRegistry = vi.fn<() => MockManifestRegistry>(() => ({
plugins: [],
diagnostics: [],
})),
loadPluginRegistrySnapshot: vi.fn(() => ({ plugins: [] })),
}));
}));
return {
loadPluginManifestRegistryForInstalledIndex: loadManifestRegistry,
loadPluginManifestRegistryForPluginRegistry: loadManifestRegistry,
loadPluginRegistrySnapshot: vi.fn(() => ({ plugins: [] })),
};
});
vi.mock("../plugins/manifest-registry-installed.js", () => ({
loadPluginManifestRegistryForInstalledIndex:
@@ -23,6 +27,8 @@ vi.mock("../plugins/manifest-registry-installed.js", () => ({
}));
vi.mock("../plugins/plugin-registry.js", () => ({
loadPluginManifestRegistryForPluginRegistry:
pluginRegistryMocks.loadPluginManifestRegistryForPluginRegistry,
loadPluginRegistrySnapshot: pluginRegistryMocks.loadPluginRegistrySnapshot,
}));

View File

@@ -1,6 +1,5 @@
import type { OpenClawConfig } from "../config/types.openclaw.js";
import { loadPluginManifestRegistryForInstalledIndex } from "../plugins/manifest-registry-installed.js";
import { loadPluginRegistrySnapshot } from "../plugins/plugin-registry.js";
import { loadPluginManifestRegistryForPluginRegistry } from "../plugins/plugin-registry.js";
export { isSafeChannelEnvVarTriggerName } from "./channel-env-var-names.js";
type ChannelEnvVarLookupParams = {
@@ -33,13 +32,7 @@ function appendUniqueEnvVarCandidates(
export function resolveChannelEnvVars(
params?: ChannelEnvVarLookupParams,
): Record<string, readonly string[]> {
const index = loadPluginRegistrySnapshot({
config: params?.config,
workspaceDir: params?.workspaceDir,
env: params?.env,
});
const registry = loadPluginManifestRegistryForInstalledIndex({
index,
const registry = loadPluginManifestRegistryForPluginRegistry({
config: params?.config,
workspaceDir: params?.workspaceDir,
env: params?.env,

View File

@@ -25,13 +25,17 @@ type MockManifestRegistry = {
diagnostics: unknown[];
};
const pluginRegistryMocks = vi.hoisted(() => ({
loadPluginManifestRegistryForInstalledIndex: vi.fn<() => MockManifestRegistry>(() => ({
const pluginRegistryMocks = vi.hoisted(() => {
const loadManifestRegistry = vi.fn<() => MockManifestRegistry>(() => ({
plugins: [],
diagnostics: [],
})),
loadPluginRegistrySnapshot: vi.fn(() => ({ plugins: [] })),
}));
}));
return {
loadPluginManifestRegistryForInstalledIndex: loadManifestRegistry,
loadPluginManifestRegistryForPluginRegistry: loadManifestRegistry,
loadPluginRegistrySnapshot: vi.fn(() => ({ plugins: [] })),
};
});
vi.mock("../plugins/manifest-registry-installed.js", () => ({
loadPluginManifestRegistryForInstalledIndex:
@@ -39,6 +43,8 @@ vi.mock("../plugins/manifest-registry-installed.js", () => ({
}));
vi.mock("../plugins/plugin-registry.js", () => ({
loadPluginManifestRegistryForPluginRegistry:
pluginRegistryMocks.loadPluginManifestRegistryForPluginRegistry,
loadPluginRegistrySnapshot: pluginRegistryMocks.loadPluginRegistrySnapshot,
}));

View File

@@ -1,12 +1,11 @@
import { resolveProviderAuthAliasMap } from "../agents/provider-auth-aliases.js";
import type { OpenClawConfig } from "../config/types.openclaw.js";
import { loadPluginManifestRegistryForInstalledIndex } from "../plugins/manifest-registry-installed.js";
import type { PluginManifestRecord } from "../plugins/manifest-registry.js";
import {
isWorkspacePluginAllowedByConfig,
normalizePluginConfigId,
} from "../plugins/plugin-config-trust.js";
import { loadPluginRegistrySnapshot } from "../plugins/plugin-registry.js";
import { loadPluginManifestRegistryForPluginRegistry } from "../plugins/plugin-registry.js";
import { hasKind } from "../plugins/slots.js";
const CORE_PROVIDER_AUTH_ENV_VAR_CANDIDATES = {
@@ -77,13 +76,7 @@ function appendUniqueEnvVarCandidates(
function resolveManifestProviderAuthEnvVarCandidates(
params?: ProviderEnvVarLookupParams,
): Record<string, string[]> {
const index = loadPluginRegistrySnapshot({
config: params?.config,
workspaceDir: params?.workspaceDir,
env: params?.env,
});
const registry = loadPluginManifestRegistryForInstalledIndex({
index,
const registry = loadPluginManifestRegistryForPluginRegistry({
config: params?.config,
workspaceDir: params?.workspaceDir,
env: params?.env,

View File

@@ -1,6 +1,5 @@
import { loadPluginManifestRegistryForInstalledIndex } from "../plugins/manifest-registry-installed.js";
import type { PluginManifestRecord } from "../plugins/manifest-registry.js";
import { loadPluginRegistrySnapshot } from "../plugins/plugin-registry.js";
import { loadPluginManifestRegistryForPluginRegistry } from "../plugins/plugin-registry.js";
import { loadBundledChannelSecretContractApi } from "./channel-contract-api.js";
import type { SecretTargetRegistryEntry } from "./target-registry-types.js";
@@ -50,11 +49,8 @@ function hasWebProviderContract(
function listBundledWebProviderSecretTargetRegistryEntries(): SecretTargetRegistryEntry[] {
const entries: SecretTargetRegistryEntry[] = [];
const index = loadPluginRegistrySnapshot({});
for (const record of loadPluginManifestRegistryForInstalledIndex({
index,
includeDisabled: true,
}).plugins) {
for (const record of loadPluginManifestRegistryForPluginRegistry({ includeDisabled: true })
.plugins) {
if (record.origin !== "bundled") {
continue;
}
@@ -73,11 +69,8 @@ function listBundledWebProviderSecretTargetRegistryEntries(): SecretTargetRegist
function listChannelSecretTargetRegistryEntries(): SecretTargetRegistryEntry[] {
const entries: SecretTargetRegistryEntry[] = [];
const index = loadPluginRegistrySnapshot({});
for (const record of loadPluginManifestRegistryForInstalledIndex({
index,
includeDisabled: true,
}).plugins) {
for (const record of loadPluginManifestRegistryForPluginRegistry({ includeDisabled: true })
.plugins) {
if (record.origin !== "bundled") {
continue;
}

View File

@@ -10,7 +10,7 @@ import {
sanitizeSupportSnapshotValue,
type SupportRedactionContext,
} from "../logging/diagnostic-support-redaction.js";
import { loadPluginManifestRegistry } from "../plugins/manifest-registry.js";
import { loadPluginManifestRegistryForPluginRegistry } from "../plugins/plugin-registry.js";
import { getActivePluginRegistry, listImportedRuntimePluginIds } from "../plugins/runtime.js";
import { VERSION } from "../version.js";
@@ -136,10 +136,11 @@ function buildPluginsFromManifest(params: {
workspaceDir?: string;
env?: NodeJS.ProcessEnv;
}) {
const registry = loadPluginManifestRegistry({
const registry = loadPluginManifestRegistryForPluginRegistry({
config: params.config,
workspaceDir: params.workspaceDir,
env: params.env,
includeDisabled: true,
});
return {
source: "manifest-registry",

View File

@@ -16,13 +16,13 @@ export type ConfigurablePlugin = {
jsonSchema?: JsonSchemaObject;
};
type ManifestRegistryModule = typeof import("../plugins/manifest-registry.js");
type PluginRegistryModule = typeof import("../plugins/plugin-registry.js");
let manifestRegistryModulePromise: Promise<ManifestRegistryModule> | undefined;
let pluginRegistryModulePromise: Promise<PluginRegistryModule> | undefined;
function loadManifestRegistryModule(): Promise<ManifestRegistryModule> {
manifestRegistryModulePromise ??= import("../plugins/manifest-registry.js");
return manifestRegistryModulePromise;
function loadPluginRegistryModule(): Promise<PluginRegistryModule> {
pluginRegistryModulePromise ??= import("../plugins/plugin-registry.js");
return pluginRegistryModulePromise;
}
type JsonSchemaProperty = {
@@ -299,10 +299,11 @@ export async function setupPluginConfig(params: {
prompter: WizardPrompter;
workspaceDir?: string;
}): Promise<OpenClawConfig> {
const { loadPluginManifestRegistry } = await loadManifestRegistryModule();
const registry = loadPluginManifestRegistry({
const { loadPluginManifestRegistryForPluginRegistry } = await loadPluginRegistryModule();
const registry = loadPluginManifestRegistryForPluginRegistry({
config: params.config,
workspaceDir: params.workspaceDir,
includeDisabled: true,
});
const unconfigured = discoverUnconfiguredPlugins({
@@ -361,10 +362,11 @@ export async function configurePluginConfig(params: {
prompter: WizardPrompter;
workspaceDir?: string;
}): Promise<OpenClawConfig> {
const { loadPluginManifestRegistry } = await loadManifestRegistryModule();
const registry = loadPluginManifestRegistry({
const { loadPluginManifestRegistryForPluginRegistry } = await loadPluginRegistryModule();
const registry = loadPluginManifestRegistryForPluginRegistry({
config: params.config,
workspaceDir: params.workspaceDir,
includeDisabled: true,
});
const configurable = discoverConfigurablePlugins({