mirror of https://github.com/openclaw/openclaw.git
302 lines
9.4 KiB
TypeScript
302 lines
9.4 KiB
TypeScript
import type { PluginDiagnostic, ProviderAuthMethod, ProviderPlugin } from "./types.js";
|
|
|
|
function pushProviderDiagnostic(params: {
|
|
level: PluginDiagnostic["level"];
|
|
pluginId: string;
|
|
source: string;
|
|
message: string;
|
|
pushDiagnostic: (diag: PluginDiagnostic) => void;
|
|
}) {
|
|
params.pushDiagnostic({
|
|
level: params.level,
|
|
pluginId: params.pluginId,
|
|
source: params.source,
|
|
message: params.message,
|
|
});
|
|
}
|
|
|
|
function normalizeText(value: string | undefined): string | undefined {
|
|
const trimmed = value?.trim();
|
|
return trimmed ? trimmed : undefined;
|
|
}
|
|
|
|
function normalizeTextList(values: string[] | undefined): string[] | undefined {
|
|
const normalized = Array.from(
|
|
new Set((values ?? []).map((value) => value.trim()).filter(Boolean)),
|
|
);
|
|
return normalized.length > 0 ? normalized : undefined;
|
|
}
|
|
|
|
function normalizeProviderWizardSetup(params: {
|
|
providerId: string;
|
|
pluginId: string;
|
|
source: string;
|
|
auth: ProviderAuthMethod[];
|
|
setup: NonNullable<ProviderPlugin["wizard"]>["setup"];
|
|
pushDiagnostic: (diag: PluginDiagnostic) => void;
|
|
}): NonNullable<ProviderPlugin["wizard"]>["setup"] {
|
|
const hasAuthMethods = params.auth.length > 0;
|
|
if (!params.setup) {
|
|
return undefined;
|
|
}
|
|
if (!hasAuthMethods) {
|
|
pushProviderDiagnostic({
|
|
level: "warn",
|
|
pluginId: params.pluginId,
|
|
source: params.source,
|
|
message: `provider "${params.providerId}" setup metadata ignored because it has no auth methods`,
|
|
pushDiagnostic: params.pushDiagnostic,
|
|
});
|
|
return undefined;
|
|
}
|
|
const methodId = normalizeText(params.setup.methodId);
|
|
if (methodId && !params.auth.some((method) => method.id === methodId)) {
|
|
pushProviderDiagnostic({
|
|
level: "warn",
|
|
pluginId: params.pluginId,
|
|
source: params.source,
|
|
message: `provider "${params.providerId}" setup method "${methodId}" not found; falling back to available methods`,
|
|
pushDiagnostic: params.pushDiagnostic,
|
|
});
|
|
}
|
|
return {
|
|
...(normalizeText(params.setup.choiceId)
|
|
? { choiceId: normalizeText(params.setup.choiceId) }
|
|
: {}),
|
|
...(normalizeText(params.setup.choiceLabel)
|
|
? { choiceLabel: normalizeText(params.setup.choiceLabel) }
|
|
: {}),
|
|
...(normalizeText(params.setup.choiceHint)
|
|
? { choiceHint: normalizeText(params.setup.choiceHint) }
|
|
: {}),
|
|
...(normalizeText(params.setup.groupId)
|
|
? { groupId: normalizeText(params.setup.groupId) }
|
|
: {}),
|
|
...(normalizeText(params.setup.groupLabel)
|
|
? { groupLabel: normalizeText(params.setup.groupLabel) }
|
|
: {}),
|
|
...(normalizeText(params.setup.groupHint)
|
|
? { groupHint: normalizeText(params.setup.groupHint) }
|
|
: {}),
|
|
...(methodId && params.auth.some((method) => method.id === methodId) ? { methodId } : {}),
|
|
...(params.setup.modelAllowlist
|
|
? {
|
|
modelAllowlist: {
|
|
...(normalizeTextList(params.setup.modelAllowlist.allowedKeys)
|
|
? { allowedKeys: normalizeTextList(params.setup.modelAllowlist.allowedKeys) }
|
|
: {}),
|
|
...(normalizeTextList(params.setup.modelAllowlist.initialSelections)
|
|
? {
|
|
initialSelections: normalizeTextList(
|
|
params.setup.modelAllowlist.initialSelections,
|
|
),
|
|
}
|
|
: {}),
|
|
...(normalizeText(params.setup.modelAllowlist.message)
|
|
? { message: normalizeText(params.setup.modelAllowlist.message) }
|
|
: {}),
|
|
},
|
|
}
|
|
: {}),
|
|
};
|
|
}
|
|
|
|
function normalizeProviderAuthMethods(params: {
|
|
providerId: string;
|
|
pluginId: string;
|
|
source: string;
|
|
auth: ProviderAuthMethod[];
|
|
pushDiagnostic: (diag: PluginDiagnostic) => void;
|
|
}): ProviderAuthMethod[] {
|
|
const seenMethodIds = new Set<string>();
|
|
const normalized: ProviderAuthMethod[] = [];
|
|
|
|
for (const method of params.auth) {
|
|
const methodId = normalizeText(method.id);
|
|
if (!methodId) {
|
|
pushProviderDiagnostic({
|
|
level: "error",
|
|
pluginId: params.pluginId,
|
|
source: params.source,
|
|
message: `provider "${params.providerId}" auth method missing id`,
|
|
pushDiagnostic: params.pushDiagnostic,
|
|
});
|
|
continue;
|
|
}
|
|
if (seenMethodIds.has(methodId)) {
|
|
pushProviderDiagnostic({
|
|
level: "error",
|
|
pluginId: params.pluginId,
|
|
source: params.source,
|
|
message: `provider "${params.providerId}" auth method duplicated id "${methodId}"`,
|
|
pushDiagnostic: params.pushDiagnostic,
|
|
});
|
|
continue;
|
|
}
|
|
seenMethodIds.add(methodId);
|
|
const wizard = normalizeProviderWizardSetup({
|
|
providerId: params.providerId,
|
|
pluginId: params.pluginId,
|
|
source: params.source,
|
|
auth: [{ ...method, id: methodId }],
|
|
setup: method.wizard,
|
|
pushDiagnostic: params.pushDiagnostic,
|
|
});
|
|
normalized.push({
|
|
...method,
|
|
id: methodId,
|
|
label: normalizeText(method.label) ?? methodId,
|
|
...(normalizeText(method.hint) ? { hint: normalizeText(method.hint) } : {}),
|
|
...(wizard ? { wizard } : {}),
|
|
});
|
|
}
|
|
|
|
return normalized;
|
|
}
|
|
|
|
function normalizeProviderWizard(params: {
|
|
providerId: string;
|
|
pluginId: string;
|
|
source: string;
|
|
auth: ProviderAuthMethod[];
|
|
wizard: ProviderPlugin["wizard"];
|
|
pushDiagnostic: (diag: PluginDiagnostic) => void;
|
|
}): ProviderPlugin["wizard"] {
|
|
if (!params.wizard) {
|
|
return undefined;
|
|
}
|
|
|
|
const hasAuthMethods = params.auth.length > 0;
|
|
const hasMethod = (methodId: string | undefined) =>
|
|
Boolean(methodId && params.auth.some((method) => method.id === methodId));
|
|
|
|
const normalizeSetup = () => {
|
|
const setup = params.wizard?.setup;
|
|
if (!setup) {
|
|
return undefined;
|
|
}
|
|
return normalizeProviderWizardSetup({
|
|
providerId: params.providerId,
|
|
pluginId: params.pluginId,
|
|
source: params.source,
|
|
auth: params.auth,
|
|
setup,
|
|
pushDiagnostic: params.pushDiagnostic,
|
|
});
|
|
};
|
|
|
|
const normalizeModelPicker = () => {
|
|
const modelPicker = params.wizard?.modelPicker;
|
|
if (!modelPicker) {
|
|
return undefined;
|
|
}
|
|
if (!hasAuthMethods) {
|
|
pushProviderDiagnostic({
|
|
level: "warn",
|
|
pluginId: params.pluginId,
|
|
source: params.source,
|
|
message: `provider "${params.providerId}" model-picker metadata ignored because it has no auth methods`,
|
|
pushDiagnostic: params.pushDiagnostic,
|
|
});
|
|
return undefined;
|
|
}
|
|
const methodId = normalizeText(modelPicker.methodId);
|
|
if (methodId && !hasMethod(methodId)) {
|
|
pushProviderDiagnostic({
|
|
level: "warn",
|
|
pluginId: params.pluginId,
|
|
source: params.source,
|
|
message: `provider "${params.providerId}" model-picker method "${methodId}" not found; falling back to available methods`,
|
|
pushDiagnostic: params.pushDiagnostic,
|
|
});
|
|
}
|
|
return {
|
|
...(normalizeText(modelPicker.label) ? { label: normalizeText(modelPicker.label) } : {}),
|
|
...(normalizeText(modelPicker.hint) ? { hint: normalizeText(modelPicker.hint) } : {}),
|
|
...(methodId && hasMethod(methodId) ? { methodId } : {}),
|
|
};
|
|
};
|
|
|
|
const setup = normalizeSetup();
|
|
const modelPicker = normalizeModelPicker();
|
|
if (!setup && !modelPicker) {
|
|
return undefined;
|
|
}
|
|
return {
|
|
...(setup ? { setup } : {}),
|
|
...(modelPicker ? { modelPicker } : {}),
|
|
};
|
|
}
|
|
|
|
export function normalizeRegisteredProvider(params: {
|
|
pluginId: string;
|
|
source: string;
|
|
provider: ProviderPlugin;
|
|
pushDiagnostic: (diag: PluginDiagnostic) => void;
|
|
}): ProviderPlugin | null {
|
|
const id = normalizeText(params.provider.id);
|
|
if (!id) {
|
|
pushProviderDiagnostic({
|
|
level: "error",
|
|
pluginId: params.pluginId,
|
|
source: params.source,
|
|
message: "provider registration missing id",
|
|
pushDiagnostic: params.pushDiagnostic,
|
|
});
|
|
return null;
|
|
}
|
|
|
|
const auth = normalizeProviderAuthMethods({
|
|
providerId: id,
|
|
pluginId: params.pluginId,
|
|
source: params.source,
|
|
auth: params.provider.auth ?? [],
|
|
pushDiagnostic: params.pushDiagnostic,
|
|
});
|
|
const docsPath = normalizeText(params.provider.docsPath);
|
|
const aliases = normalizeTextList(params.provider.aliases);
|
|
const deprecatedProfileIds = normalizeTextList(params.provider.deprecatedProfileIds);
|
|
const envVars = normalizeTextList(params.provider.envVars);
|
|
const wizard = normalizeProviderWizard({
|
|
providerId: id,
|
|
pluginId: params.pluginId,
|
|
source: params.source,
|
|
auth,
|
|
wizard: params.provider.wizard,
|
|
pushDiagnostic: params.pushDiagnostic,
|
|
});
|
|
const catalog = params.provider.catalog;
|
|
const discovery = params.provider.discovery;
|
|
if (catalog && discovery) {
|
|
pushProviderDiagnostic({
|
|
level: "warn",
|
|
pluginId: params.pluginId,
|
|
source: params.source,
|
|
message: `provider "${id}" registered both catalog and discovery; using catalog`,
|
|
pushDiagnostic: params.pushDiagnostic,
|
|
});
|
|
}
|
|
const {
|
|
wizard: _ignoredWizard,
|
|
docsPath: _ignoredDocsPath,
|
|
aliases: _ignoredAliases,
|
|
envVars: _ignoredEnvVars,
|
|
catalog: _ignoredCatalog,
|
|
discovery: _ignoredDiscovery,
|
|
...restProvider
|
|
} = params.provider;
|
|
return {
|
|
...restProvider,
|
|
id,
|
|
label: normalizeText(params.provider.label) ?? id,
|
|
...(docsPath ? { docsPath } : {}),
|
|
...(aliases ? { aliases } : {}),
|
|
...(deprecatedProfileIds ? { deprecatedProfileIds } : {}),
|
|
...(envVars ? { envVars } : {}),
|
|
auth,
|
|
...(catalog ? { catalog } : {}),
|
|
...(!catalog && discovery ? { discovery } : {}),
|
|
...(wizard ? { wizard } : {}),
|
|
};
|
|
}
|