






















@@ -0,0 +1,123 @@
1+import { isBlockedObjectKey } from "../../infra/prototype-keys.js";
2+import { normalizeOptionalString } from "../../shared/string-coerce.js";
3+import { normalizeTrimmedStringList } from "../../shared/string-normalization.js";
4+import { isRecord } from "../../utils.js";
5+import { normalizeModelCatalog } from "../normalize.js";
6+import { normalizeModelCatalogProviderId } from "../refs.js";
7+import type { ModelCatalogProvider } from "../types.js";
8+import type {
9+OpenClawProviderIndex,
10+OpenClawProviderIndexPlugin,
11+OpenClawProviderIndexProvider,
12+} from "./types.js";
13+14+const OPENCLAW_PROVIDER_INDEX_VERSION = 1;
15+16+function normalizeSafeKey(value: unknown): string {
17+const key = normalizeOptionalString(value) ?? "";
18+return key && !isBlockedObjectKey(key) ? key : "";
19+}
20+21+function normalizePlugin(value: unknown): OpenClawProviderIndexPlugin | undefined {
22+if (!isRecord(value)) {
23+return undefined;
24+}
25+const id = normalizeSafeKey(value.id);
26+if (!id) {
27+return undefined;
28+}
29+const packageName = normalizeOptionalString(value.package) ?? "";
30+const source = normalizeOptionalString(value.source) ?? "";
31+return {
32+ id,
33+ ...(packageName ? { package: packageName } : {}),
34+ ...(source ? { source } : {}),
35+};
36+}
37+38+function normalizeCategories(value: unknown): readonly string[] {
39+return [...new Set(normalizeTrimmedStringList(value))];
40+}
41+42+function normalizePreviewCatalog(params: {
43+providerId: string;
44+value: unknown;
45+}): ModelCatalogProvider | undefined {
46+const catalog = normalizeModelCatalog(
47+{ providers: { [params.providerId]: params.value } },
48+{ ownedProviders: new Set([params.providerId]) },
49+);
50+const provider = catalog?.providers?.[params.providerId];
51+if (!provider) {
52+return undefined;
53+}
54+return {
55+ ...provider,
56+models: provider.models.map((model) => ({
57+ ...model,
58+status: model.status ?? "preview",
59+})),
60+};
61+}
62+63+function normalizeProvider(
64+rawProviderId: string,
65+value: unknown,
66+): OpenClawProviderIndexProvider | undefined {
67+if (!isRecord(value)) {
68+return undefined;
69+}
70+const providerId = normalizeModelCatalogProviderId(rawProviderId);
71+if (!providerId) {
72+return undefined;
73+}
74+const id = normalizeModelCatalogProviderId(normalizeOptionalString(value.id) ?? "");
75+if (id && id !== providerId) {
76+return undefined;
77+}
78+const name = normalizeOptionalString(value.name) ?? "";
79+const plugin = normalizePlugin(value.plugin);
80+if (!name || !plugin) {
81+return undefined;
82+}
83+const docs = normalizeOptionalString(value.docs) ?? "";
84+const categories = normalizeCategories(value.categories);
85+const previewCatalog = normalizePreviewCatalog({
86+ providerId,
87+value: value.previewCatalog,
88+});
89+return {
90+id: providerId,
91+ name,
92+ plugin,
93+ ...(docs ? { docs } : {}),
94+ ...(categories.length > 0 ? { categories } : {}),
95+ ...(previewCatalog ? { previewCatalog } : {}),
96+};
97+}
98+99+export function normalizeOpenClawProviderIndex(value: unknown): OpenClawProviderIndex | undefined {
100+if (!isRecord(value) || value.version !== OPENCLAW_PROVIDER_INDEX_VERSION) {
101+return undefined;
102+}
103+if (!isRecord(value.providers)) {
104+return undefined;
105+}
106+const providers: Record<string, OpenClawProviderIndexProvider> = {};
107+for (const [rawProviderId, rawProvider] of Object.entries(value.providers)) {
108+const providerId = normalizeModelCatalogProviderId(rawProviderId);
109+if (!providerId || isBlockedObjectKey(providerId)) {
110+continue;
111+}
112+const provider = normalizeProvider(providerId, rawProvider);
113+if (provider) {
114+providers[providerId] = provider;
115+}
116+}
117+return {
118+version: OPENCLAW_PROVIDER_INDEX_VERSION,
119+providers: Object.fromEntries(
120+Object.entries(providers).toSorted(([left], [right]) => left.localeCompare(right)),
121+),
122+};
123+}
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。