mirror of
https://github.com/moltbot/moltbot.git
synced 2026-04-23 14:45:46 +00:00
feat: add bundled Chutes extension (#49136)
* refactor: generalize bundled provider discovery seams * feat: land chutes extension via plugin-owned auth (#41416) (thanks @Veightor)
This commit is contained in:
committed by
GitHub
parent
ea15819ecf
commit
a724bbce1a
@@ -270,4 +270,9 @@ describe("resolveEnableState", () => {
|
||||
const state = resolveEnableState("google", "bundled", normalizePluginsConfig({}));
|
||||
expect(state).toEqual({ enabled: true });
|
||||
});
|
||||
|
||||
it("allows bundled plugins to opt into default enablement from manifest metadata", () => {
|
||||
const state = resolveEnableState("profile-aware", "bundled", normalizePluginsConfig({}), true);
|
||||
expect(state).toEqual({ enabled: true });
|
||||
});
|
||||
});
|
||||
|
||||
@@ -274,6 +274,7 @@ export function resolveEnableState(
|
||||
id: string,
|
||||
origin: PluginRecord["origin"],
|
||||
config: NormalizedPluginsConfig,
|
||||
enabledByDefault?: boolean,
|
||||
): { enabled: boolean; reason?: string } {
|
||||
if (!config.enabled) {
|
||||
return { enabled: false, reason: "plugins disabled" };
|
||||
@@ -298,7 +299,7 @@ export function resolveEnableState(
|
||||
if (entry?.enabled === true) {
|
||||
return { enabled: true };
|
||||
}
|
||||
if (origin === "bundled" && BUNDLED_ENABLED_BY_DEFAULT.has(id)) {
|
||||
if (origin === "bundled" && (enabledByDefault ?? BUNDLED_ENABLED_BY_DEFAULT.has(id))) {
|
||||
return { enabled: true };
|
||||
}
|
||||
if (origin === "bundled") {
|
||||
@@ -331,8 +332,9 @@ export function resolveEffectiveEnableState(params: {
|
||||
origin: PluginRecord["origin"];
|
||||
config: NormalizedPluginsConfig;
|
||||
rootConfig?: OpenClawConfig;
|
||||
enabledByDefault?: boolean;
|
||||
}): { enabled: boolean; reason?: string } {
|
||||
const base = resolveEnableState(params.id, params.origin, params.config);
|
||||
const base = resolveEnableState(params.id, params.origin, params.config, params.enabledByDefault);
|
||||
if (
|
||||
!base.enabled &&
|
||||
base.reason === "bundled (disabled by default)" &&
|
||||
|
||||
@@ -131,12 +131,30 @@ function runCatalog(params: {
|
||||
provider: Awaited<ReturnType<typeof requireProvider>>;
|
||||
env?: NodeJS.ProcessEnv;
|
||||
resolveProviderApiKey?: () => { apiKey: string | undefined };
|
||||
resolveProviderAuth?: (
|
||||
providerId?: string,
|
||||
options?: { oauthMarker?: string },
|
||||
) => {
|
||||
apiKey: string | undefined;
|
||||
discoveryApiKey?: string;
|
||||
mode: "api_key" | "oauth" | "token" | "none";
|
||||
source: "env" | "profile" | "none";
|
||||
profileId?: string;
|
||||
};
|
||||
}) {
|
||||
return runProviderCatalog({
|
||||
provider: params.provider,
|
||||
config: {},
|
||||
env: params.env ?? ({} as NodeJS.ProcessEnv),
|
||||
resolveProviderApiKey: params.resolveProviderApiKey ?? (() => ({ apiKey: undefined })),
|
||||
resolveProviderAuth:
|
||||
params.resolveProviderAuth ??
|
||||
((_, options) => ({
|
||||
apiKey: options?.oauthMarker,
|
||||
discoveryApiKey: undefined,
|
||||
mode: options?.oauthMarker ? "oauth" : "none",
|
||||
source: options?.oauthMarker ? "profile" : "none",
|
||||
})),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -249,6 +267,12 @@ describe("provider discovery contract", () => {
|
||||
},
|
||||
env: {} as NodeJS.ProcessEnv,
|
||||
resolveProviderApiKey: () => ({ apiKey: undefined }),
|
||||
resolveProviderAuth: () => ({
|
||||
apiKey: undefined,
|
||||
discoveryApiKey: undefined,
|
||||
mode: "none",
|
||||
source: "none",
|
||||
}),
|
||||
}),
|
||||
).resolves.toMatchObject({
|
||||
provider: {
|
||||
@@ -274,6 +298,12 @@ describe("provider discovery contract", () => {
|
||||
config: {},
|
||||
env: {} as NodeJS.ProcessEnv,
|
||||
resolveProviderApiKey: () => ({ apiKey: undefined }),
|
||||
resolveProviderAuth: () => ({
|
||||
apiKey: undefined,
|
||||
discoveryApiKey: undefined,
|
||||
mode: "none",
|
||||
source: "none",
|
||||
}),
|
||||
}),
|
||||
).resolves.toBeNull();
|
||||
expect(buildOllamaProviderMock).toHaveBeenCalledWith(undefined, { quiet: true });
|
||||
@@ -297,6 +327,12 @@ describe("provider discovery contract", () => {
|
||||
apiKey: "VLLM_API_KEY",
|
||||
discoveryApiKey: "env-vllm-key",
|
||||
}),
|
||||
resolveProviderAuth: () => ({
|
||||
apiKey: "VLLM_API_KEY",
|
||||
discoveryApiKey: "env-vllm-key",
|
||||
mode: "api_key",
|
||||
source: "env",
|
||||
}),
|
||||
}),
|
||||
).resolves.toEqual({
|
||||
provider: {
|
||||
@@ -329,6 +365,12 @@ describe("provider discovery contract", () => {
|
||||
apiKey: "SGLANG_API_KEY",
|
||||
discoveryApiKey: "env-sglang-key",
|
||||
}),
|
||||
resolveProviderAuth: () => ({
|
||||
apiKey: "SGLANG_API_KEY",
|
||||
discoveryApiKey: "env-sglang-key",
|
||||
mode: "api_key",
|
||||
source: "env",
|
||||
}),
|
||||
}),
|
||||
).resolves.toEqual({
|
||||
provider: {
|
||||
@@ -352,6 +394,12 @@ describe("provider discovery contract", () => {
|
||||
MINIMAX_API_KEY: "minimax-key",
|
||||
} as NodeJS.ProcessEnv,
|
||||
resolveProviderApiKey: () => ({ apiKey: "minimax-key" }),
|
||||
resolveProviderAuth: () => ({
|
||||
apiKey: "minimax-key",
|
||||
discoveryApiKey: undefined,
|
||||
mode: "api_key",
|
||||
source: "env",
|
||||
}),
|
||||
}),
|
||||
).resolves.toMatchObject({
|
||||
provider: {
|
||||
@@ -391,6 +439,13 @@ describe("provider discovery contract", () => {
|
||||
config: {},
|
||||
env: {} as NodeJS.ProcessEnv,
|
||||
resolveProviderApiKey: () => ({ apiKey: undefined }),
|
||||
resolveProviderAuth: () => ({
|
||||
apiKey: "minimax-oauth",
|
||||
discoveryApiKey: "access-token",
|
||||
mode: "oauth",
|
||||
source: "profile",
|
||||
profileId: "minimax-portal:default",
|
||||
}),
|
||||
}),
|
||||
).resolves.toMatchObject({
|
||||
provider: {
|
||||
@@ -420,6 +475,12 @@ describe("provider discovery contract", () => {
|
||||
},
|
||||
env: {} as NodeJS.ProcessEnv,
|
||||
resolveProviderApiKey: () => ({ apiKey: undefined }),
|
||||
resolveProviderAuth: () => ({
|
||||
apiKey: undefined,
|
||||
discoveryApiKey: undefined,
|
||||
mode: "none",
|
||||
source: "none",
|
||||
}),
|
||||
}),
|
||||
).resolves.toMatchObject({
|
||||
provider: {
|
||||
@@ -447,6 +508,12 @@ describe("provider discovery contract", () => {
|
||||
MODELSTUDIO_API_KEY: "modelstudio-key",
|
||||
} as NodeJS.ProcessEnv,
|
||||
resolveProviderApiKey: () => ({ apiKey: "modelstudio-key" }),
|
||||
resolveProviderAuth: () => ({
|
||||
apiKey: "modelstudio-key",
|
||||
discoveryApiKey: undefined,
|
||||
mode: "api_key",
|
||||
source: "env",
|
||||
}),
|
||||
}),
|
||||
).resolves.toMatchObject({
|
||||
provider: {
|
||||
@@ -468,6 +535,12 @@ describe("provider discovery contract", () => {
|
||||
config: {},
|
||||
env: {} as NodeJS.ProcessEnv,
|
||||
resolveProviderApiKey: () => ({ apiKey: undefined }),
|
||||
resolveProviderAuth: () => ({
|
||||
apiKey: undefined,
|
||||
discoveryApiKey: undefined,
|
||||
mode: "none",
|
||||
source: "none",
|
||||
}),
|
||||
}),
|
||||
).resolves.toBeNull();
|
||||
});
|
||||
@@ -504,6 +577,12 @@ describe("provider discovery contract", () => {
|
||||
CLOUDFLARE_AI_GATEWAY_API_KEY: "secret-value",
|
||||
} as NodeJS.ProcessEnv,
|
||||
resolveProviderApiKey: () => ({ apiKey: undefined }),
|
||||
resolveProviderAuth: () => ({
|
||||
apiKey: undefined,
|
||||
discoveryApiKey: undefined,
|
||||
mode: "none",
|
||||
source: "none",
|
||||
}),
|
||||
}),
|
||||
).resolves.toEqual({
|
||||
provider: {
|
||||
|
||||
@@ -1,41 +1,18 @@
|
||||
import amazonBedrockPlugin from "../../../extensions/amazon-bedrock/index.js";
|
||||
import anthropicPlugin from "../../../extensions/anthropic/index.js";
|
||||
import bravePlugin from "../../../extensions/brave/index.js";
|
||||
import byteplusPlugin from "../../../extensions/byteplus/index.js";
|
||||
import cloudflareAiGatewayPlugin from "../../../extensions/cloudflare-ai-gateway/index.js";
|
||||
import copilotProxyPlugin from "../../../extensions/copilot-proxy/index.js";
|
||||
import elevenLabsPlugin from "../../../extensions/elevenlabs/index.js";
|
||||
import firecrawlPlugin from "../../../extensions/firecrawl/index.js";
|
||||
import githubCopilotPlugin from "../../../extensions/github-copilot/index.js";
|
||||
import googlePlugin from "../../../extensions/google/index.js";
|
||||
import huggingFacePlugin from "../../../extensions/huggingface/index.js";
|
||||
import kilocodePlugin from "../../../extensions/kilocode/index.js";
|
||||
import kimiCodingPlugin from "../../../extensions/kimi-coding/index.js";
|
||||
import microsoftPlugin from "../../../extensions/microsoft/index.js";
|
||||
import minimaxPlugin from "../../../extensions/minimax/index.js";
|
||||
import mistralPlugin from "../../../extensions/mistral/index.js";
|
||||
import modelStudioPlugin from "../../../extensions/modelstudio/index.js";
|
||||
import moonshotPlugin from "../../../extensions/moonshot/index.js";
|
||||
import nvidiaPlugin from "../../../extensions/nvidia/index.js";
|
||||
import ollamaPlugin from "../../../extensions/ollama/index.js";
|
||||
import openAIPlugin from "../../../extensions/openai/index.js";
|
||||
import opencodeGoPlugin from "../../../extensions/opencode-go/index.js";
|
||||
import opencodePlugin from "../../../extensions/opencode/index.js";
|
||||
import openRouterPlugin from "../../../extensions/openrouter/index.js";
|
||||
import perplexityPlugin from "../../../extensions/perplexity/index.js";
|
||||
import qianfanPlugin from "../../../extensions/qianfan/index.js";
|
||||
import qwenPortalPlugin from "../../../extensions/qwen-portal-auth/index.js";
|
||||
import sglangPlugin from "../../../extensions/sglang/index.js";
|
||||
import syntheticPlugin from "../../../extensions/synthetic/index.js";
|
||||
import togetherPlugin from "../../../extensions/together/index.js";
|
||||
import venicePlugin from "../../../extensions/venice/index.js";
|
||||
import vercelAiGatewayPlugin from "../../../extensions/vercel-ai-gateway/index.js";
|
||||
import vllmPlugin from "../../../extensions/vllm/index.js";
|
||||
import volcenginePlugin from "../../../extensions/volcengine/index.js";
|
||||
import xaiPlugin from "../../../extensions/xai/index.js";
|
||||
import xiaomiPlugin from "../../../extensions/xiaomi/index.js";
|
||||
import zaiPlugin from "../../../extensions/zai/index.js";
|
||||
import { createCapturedPluginRegistration } from "../captured-registration.js";
|
||||
import { resolvePluginProviders } from "../providers.js";
|
||||
import type {
|
||||
ImageGenerationProviderPlugin,
|
||||
MediaUnderstandingProviderPlugin,
|
||||
@@ -75,41 +52,6 @@ type PluginRegistrationContractEntry = {
|
||||
toolNames: string[];
|
||||
};
|
||||
|
||||
const bundledProviderPlugins: RegistrablePlugin[] = [
|
||||
amazonBedrockPlugin,
|
||||
anthropicPlugin,
|
||||
byteplusPlugin,
|
||||
cloudflareAiGatewayPlugin,
|
||||
copilotProxyPlugin,
|
||||
githubCopilotPlugin,
|
||||
googlePlugin,
|
||||
huggingFacePlugin,
|
||||
kilocodePlugin,
|
||||
kimiCodingPlugin,
|
||||
minimaxPlugin,
|
||||
mistralPlugin,
|
||||
modelStudioPlugin,
|
||||
moonshotPlugin,
|
||||
nvidiaPlugin,
|
||||
ollamaPlugin,
|
||||
opencodeGoPlugin,
|
||||
opencodePlugin,
|
||||
openAIPlugin,
|
||||
openRouterPlugin,
|
||||
qianfanPlugin,
|
||||
qwenPortalPlugin,
|
||||
sglangPlugin,
|
||||
syntheticPlugin,
|
||||
togetherPlugin,
|
||||
venicePlugin,
|
||||
vercelAiGatewayPlugin,
|
||||
vllmPlugin,
|
||||
volcenginePlugin,
|
||||
xaiPlugin,
|
||||
xiaomiPlugin,
|
||||
zaiPlugin,
|
||||
];
|
||||
|
||||
const bundledWebSearchPlugins: Array<RegistrablePlugin & { credentialValue: unknown }> = [
|
||||
{ ...bravePlugin, credentialValue: "BSA-test" },
|
||||
{ ...firecrawlPlugin, credentialValue: "fc-test" },
|
||||
@@ -153,10 +95,30 @@ function buildCapabilityContractRegistry<T>(params: {
|
||||
}
|
||||
|
||||
export const providerContractRegistry: ProviderContractEntry[] = buildCapabilityContractRegistry({
|
||||
plugins: bundledProviderPlugins,
|
||||
select: (captured) => captured.providers,
|
||||
plugins: [],
|
||||
select: () => [],
|
||||
});
|
||||
|
||||
const loadedBundledProviderRegistry: ProviderContractEntry[] = resolvePluginProviders({
|
||||
bundledProviderAllowlistCompat: true,
|
||||
bundledProviderVitestCompat: true,
|
||||
cache: false,
|
||||
activate: false,
|
||||
})
|
||||
.filter((provider): provider is ProviderPlugin & { pluginId: string } =>
|
||||
Boolean(provider.pluginId),
|
||||
)
|
||||
.map((provider) => ({
|
||||
pluginId: provider.pluginId,
|
||||
provider,
|
||||
}));
|
||||
|
||||
providerContractRegistry.splice(
|
||||
0,
|
||||
providerContractRegistry.length,
|
||||
...loadedBundledProviderRegistry,
|
||||
);
|
||||
|
||||
export const uniqueProviderContractProviders: ProviderPlugin[] = [
|
||||
...new Map(providerContractRegistry.map((entry) => [entry.provider.id, entry.provider])).values(),
|
||||
];
|
||||
@@ -234,7 +196,6 @@ export const imageGenerationProviderContractRegistry: ImageGenerationProviderCon
|
||||
const bundledPluginRegistrationList = [
|
||||
...new Map(
|
||||
[
|
||||
...bundledProviderPlugins,
|
||||
...bundledSpeechPlugins,
|
||||
...bundledMediaUnderstandingPlugins,
|
||||
...bundledImageGenerationPlugins,
|
||||
@@ -243,18 +204,47 @@ const bundledPluginRegistrationList = [
|
||||
).values(),
|
||||
];
|
||||
|
||||
export const pluginRegistrationContractRegistry: PluginRegistrationContractEntry[] =
|
||||
bundledPluginRegistrationList.map((plugin) => {
|
||||
const captured = captureRegistrations(plugin);
|
||||
return {
|
||||
pluginId: plugin.id,
|
||||
providerIds: captured.providers.map((provider) => provider.id),
|
||||
speechProviderIds: captured.speechProviders.map((provider) => provider.id),
|
||||
mediaUnderstandingProviderIds: captured.mediaUnderstandingProviders.map(
|
||||
(provider) => provider.id,
|
||||
),
|
||||
imageGenerationProviderIds: captured.imageGenerationProviders.map((provider) => provider.id),
|
||||
webSearchProviderIds: captured.webSearchProviders.map((provider) => provider.id),
|
||||
toolNames: captured.tools.map((tool) => tool.name),
|
||||
};
|
||||
});
|
||||
export const pluginRegistrationContractRegistry: PluginRegistrationContractEntry[] = [
|
||||
...new Map(
|
||||
providerContractRegistry.map((entry) => [
|
||||
entry.pluginId,
|
||||
{
|
||||
pluginId: entry.pluginId,
|
||||
providerIds: providerContractRegistry
|
||||
.filter((candidate) => candidate.pluginId === entry.pluginId)
|
||||
.map((candidate) => candidate.provider.id),
|
||||
speechProviderIds: [] as string[],
|
||||
mediaUnderstandingProviderIds: [] as string[],
|
||||
imageGenerationProviderIds: [] as string[],
|
||||
webSearchProviderIds: [] as string[],
|
||||
toolNames: [] as string[],
|
||||
},
|
||||
]),
|
||||
).values(),
|
||||
];
|
||||
|
||||
for (const plugin of bundledPluginRegistrationList) {
|
||||
const captured = captureRegistrations(plugin);
|
||||
const existing = pluginRegistrationContractRegistry.find((entry) => entry.pluginId === plugin.id);
|
||||
const next = {
|
||||
pluginId: plugin.id,
|
||||
providerIds: captured.providers.map((provider) => provider.id),
|
||||
speechProviderIds: captured.speechProviders.map((provider) => provider.id),
|
||||
mediaUnderstandingProviderIds: captured.mediaUnderstandingProviders.map(
|
||||
(provider) => provider.id,
|
||||
),
|
||||
imageGenerationProviderIds: captured.imageGenerationProviders.map((provider) => provider.id),
|
||||
webSearchProviderIds: captured.webSearchProviders.map((provider) => provider.id),
|
||||
toolNames: captured.tools.map((tool) => tool.name),
|
||||
};
|
||||
if (!existing) {
|
||||
pluginRegistrationContractRegistry.push(next);
|
||||
continue;
|
||||
}
|
||||
existing.providerIds = next.providerIds.length > 0 ? next.providerIds : existing.providerIds;
|
||||
existing.speechProviderIds = next.speechProviderIds;
|
||||
existing.mediaUnderstandingProviderIds = next.mediaUnderstandingProviderIds;
|
||||
existing.imageGenerationProviderIds = next.imageGenerationProviderIds;
|
||||
existing.webSearchProviderIds = next.webSearchProviderIds;
|
||||
existing.toolNames = next.toolNames;
|
||||
}
|
||||
|
||||
@@ -2877,6 +2877,44 @@ module.exports = {
|
||||
}
|
||||
});
|
||||
|
||||
it("loads bundled plugins when manifest metadata opts into default enablement", () => {
|
||||
const bundledDir = makeTempDir();
|
||||
const plugin = writePlugin({
|
||||
id: "profile-aware",
|
||||
body: `module.exports = { id: "profile-aware", register() {} };`,
|
||||
dir: bundledDir,
|
||||
filename: "index.cjs",
|
||||
});
|
||||
fs.writeFileSync(
|
||||
path.join(plugin.dir, "openclaw.plugin.json"),
|
||||
JSON.stringify(
|
||||
{
|
||||
id: "profile-aware",
|
||||
enabledByDefault: true,
|
||||
configSchema: EMPTY_PLUGIN_SCHEMA,
|
||||
},
|
||||
null,
|
||||
2,
|
||||
),
|
||||
"utf-8",
|
||||
);
|
||||
process.env.OPENCLAW_BUNDLED_PLUGINS_DIR = bundledDir;
|
||||
|
||||
const registry = loadOpenClawPlugins({
|
||||
cache: false,
|
||||
workspaceDir: bundledDir,
|
||||
config: {
|
||||
plugins: {
|
||||
enabled: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const bundledPlugin = registry.plugins.find((entry) => entry.id === "profile-aware");
|
||||
expect(bundledPlugin?.origin).toBe("bundled");
|
||||
expect(bundledPlugin?.status).toBe("loaded");
|
||||
});
|
||||
|
||||
it("keeps scoped and unscoped plugin ids distinct", () => {
|
||||
useNoBundledPlugins();
|
||||
const scoped = writePlugin({
|
||||
|
||||
@@ -1035,6 +1035,7 @@ export function loadOpenClawPlugins(options: PluginLoadOptions = {}): PluginRegi
|
||||
origin: candidate.origin,
|
||||
config: normalized,
|
||||
rootConfig: cfg,
|
||||
enabledByDefault: manifestRecord.enabledByDefault,
|
||||
});
|
||||
const entry = normalized.entries[pluginId];
|
||||
const record = createPluginRecord({
|
||||
|
||||
@@ -203,6 +203,7 @@ describe("loadPluginManifestRegistry", () => {
|
||||
const dir = makeTempDir();
|
||||
writeManifest(dir, {
|
||||
id: "openai",
|
||||
enabledByDefault: true,
|
||||
providers: ["openai", "openai-codex"],
|
||||
providerAuthEnvVars: {
|
||||
openai: ["OPENAI_API_KEY"],
|
||||
@@ -227,6 +228,7 @@ describe("loadPluginManifestRegistry", () => {
|
||||
expect(registry.plugins[0]?.providerAuthEnvVars).toEqual({
|
||||
openai: ["OPENAI_API_KEY"],
|
||||
});
|
||||
expect(registry.plugins[0]?.enabledByDefault).toBe(true);
|
||||
expect(registry.plugins[0]?.providerAuthChoices).toEqual([
|
||||
{
|
||||
provider: "openai",
|
||||
|
||||
@@ -35,6 +35,7 @@ export type PluginManifestRecord = {
|
||||
name?: string;
|
||||
description?: string;
|
||||
version?: string;
|
||||
enabledByDefault?: boolean;
|
||||
format?: PluginFormat;
|
||||
bundleFormat?: PluginBundleFormat;
|
||||
bundleCapabilities?: string[];
|
||||
@@ -154,6 +155,7 @@ function buildRecord(params: {
|
||||
description:
|
||||
normalizeManifestLabel(params.manifest.description) ?? params.candidate.packageDescription,
|
||||
version: normalizeManifestLabel(params.manifest.version) ?? params.candidate.packageVersion,
|
||||
enabledByDefault: params.manifest.enabledByDefault === true ? true : undefined,
|
||||
format: params.candidate.format ?? "openclaw",
|
||||
bundleFormat: params.candidate.bundleFormat,
|
||||
kind: params.manifest.kind,
|
||||
|
||||
@@ -11,6 +11,7 @@ export const PLUGIN_MANIFEST_FILENAMES = [PLUGIN_MANIFEST_FILENAME] as const;
|
||||
export type PluginManifest = {
|
||||
id: string;
|
||||
configSchema: Record<string, unknown>;
|
||||
enabledByDefault?: boolean;
|
||||
kind?: PluginKind;
|
||||
channels?: string[];
|
||||
providers?: string[];
|
||||
@@ -180,6 +181,7 @@ export function loadPluginManifest(
|
||||
}
|
||||
|
||||
const kind = typeof raw.kind === "string" ? (raw.kind as PluginKind) : undefined;
|
||||
const enabledByDefault = raw.enabledByDefault === true;
|
||||
const name = typeof raw.name === "string" ? raw.name.trim() : undefined;
|
||||
const description = typeof raw.description === "string" ? raw.description.trim() : undefined;
|
||||
const version = typeof raw.version === "string" ? raw.version.trim() : undefined;
|
||||
@@ -199,6 +201,7 @@ export function loadPluginManifest(
|
||||
manifest: {
|
||||
id,
|
||||
configSchema,
|
||||
...(enabledByDefault ? { enabledByDefault } : {}),
|
||||
kind,
|
||||
channels,
|
||||
providers,
|
||||
|
||||
@@ -27,6 +27,11 @@ function createCatalogContext(params: {
|
||||
resolveProviderApiKey: (providerId) => ({
|
||||
apiKey: providerId ? params.apiKeys?.[providerId] : undefined,
|
||||
}),
|
||||
resolveProviderAuth: (providerId) => ({
|
||||
apiKey: providerId ? params.apiKeys?.[providerId] : undefined,
|
||||
mode: providerId && params.apiKeys?.[providerId] ? "api_key" : "none",
|
||||
source: providerId && params.apiKeys?.[providerId] ? "env" : "none",
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -120,6 +120,12 @@ describe("runProviderCatalog", () => {
|
||||
config: {},
|
||||
env: {},
|
||||
resolveProviderApiKey: () => ({ apiKey: undefined }),
|
||||
resolveProviderAuth: () => ({
|
||||
apiKey: undefined,
|
||||
discoveryApiKey: undefined,
|
||||
mode: "none",
|
||||
source: "none",
|
||||
}),
|
||||
});
|
||||
|
||||
expect(result).toEqual({
|
||||
|
||||
@@ -81,6 +81,16 @@ export function runProviderCatalog(params: {
|
||||
apiKey: string | undefined;
|
||||
discoveryApiKey?: string;
|
||||
};
|
||||
resolveProviderAuth: (
|
||||
providerId?: string,
|
||||
options?: { oauthMarker?: string },
|
||||
) => {
|
||||
apiKey: string | undefined;
|
||||
discoveryApiKey?: string;
|
||||
mode: "api_key" | "oauth" | "token" | "none";
|
||||
source: "env" | "profile" | "none";
|
||||
profileId?: string;
|
||||
};
|
||||
}) {
|
||||
return resolveProviderCatalogHook(params.provider)?.run({
|
||||
config: params.config,
|
||||
@@ -88,5 +98,6 @@ export function runProviderCatalog(params: {
|
||||
workspaceDir: params.workspaceDir,
|
||||
env: params.env,
|
||||
resolveProviderApiKey: params.resolveProviderApiKey,
|
||||
resolveProviderAuth: params.resolveProviderAuth,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -70,6 +70,11 @@ describe("resolvePluginProviders", () => {
|
||||
config: expect.objectContaining({
|
||||
plugins: expect.objectContaining({
|
||||
allow: expect.arrayContaining(["openrouter", "google", "kilocode", "moonshot"]),
|
||||
entries: expect.objectContaining({
|
||||
google: { enabled: true },
|
||||
kilocode: { enabled: true },
|
||||
moonshot: { enabled: true },
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
cache: false,
|
||||
@@ -89,6 +94,10 @@ describe("resolvePluginProviders", () => {
|
||||
plugins: expect.objectContaining({
|
||||
enabled: true,
|
||||
allow: expect.arrayContaining(["google", "moonshot"]),
|
||||
entries: expect.objectContaining({
|
||||
google: { enabled: true },
|
||||
moonshot: { enabled: true },
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
cache: false,
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import { normalizeProviderId } from "../agents/provider-id.js";
|
||||
import { createSubsystemLogger } from "../logging/subsystem.js";
|
||||
import { withBundledPluginAllowlistCompat } from "./bundled-compat.js";
|
||||
import {
|
||||
withBundledPluginAllowlistCompat,
|
||||
withBundledPluginEnablementCompat,
|
||||
} from "./bundled-compat.js";
|
||||
import { normalizePluginsConfig, resolveEffectiveEnableState } from "./config-state.js";
|
||||
import { loadOpenClawPlugins, type PluginLoadOptions } from "./loader.js";
|
||||
import { createPluginLoaderLogger } from "./logger.js";
|
||||
@@ -165,13 +168,20 @@ export function resolvePluginProviders(params: {
|
||||
pluginIds: bundledProviderCompatPluginIds,
|
||||
})
|
||||
: params.config;
|
||||
const config = params.bundledProviderVitestCompat
|
||||
const maybeVitestCompat = params.bundledProviderVitestCompat
|
||||
? withBundledProviderVitestCompat({
|
||||
config: maybeAllowlistCompat,
|
||||
pluginIds: bundledProviderCompatPluginIds,
|
||||
env: params.env,
|
||||
})
|
||||
: maybeAllowlistCompat;
|
||||
const config =
|
||||
params.bundledProviderAllowlistCompat || params.bundledProviderVitestCompat
|
||||
? withBundledPluginEnablementCompat({
|
||||
config: maybeVitestCompat,
|
||||
pluginIds: bundledProviderCompatPluginIds,
|
||||
})
|
||||
: maybeVitestCompat;
|
||||
const registry = loadOpenClawPlugins({
|
||||
config,
|
||||
workspaceDir: params.workspaceDir,
|
||||
|
||||
@@ -246,6 +246,18 @@ export type ProviderCatalogContext = {
|
||||
apiKey: string | undefined;
|
||||
discoveryApiKey?: string;
|
||||
};
|
||||
resolveProviderAuth: (
|
||||
providerId?: string,
|
||||
options?: {
|
||||
oauthMarker?: string;
|
||||
},
|
||||
) => {
|
||||
apiKey: string | undefined;
|
||||
discoveryApiKey?: string;
|
||||
mode: "api_key" | "oauth" | "token" | "none";
|
||||
source: "env" | "profile" | "none";
|
||||
profileId?: string;
|
||||
};
|
||||
};
|
||||
|
||||
export type ProviderCatalogResult =
|
||||
|
||||
Reference in New Issue
Block a user