Main recovery: restore formatter and contract checks (#49570)

* Extensions: fix oxfmt drift on main

* Plugins: restore runtime barrel exports on main

* Config: restore web search compatibility types

* Telegram: align test harness with reply runtime

* Plugin SDK: fix channel config accessor generics

* CLI: remove redundant search provider casts

* Tests: restore main typecheck coverage

* Lobster: fix test import formatting

* Extensions: route bundled seams through plugin-sdk

* Tests: use extension env helper for xai

* Image generation: fix main oxfmt drift

* Config: restore latest main compatibility checks

* Plugin SDK: align guardrail tests with lint

* Telegram: type native command skill mock
This commit is contained in:
Vincent Koc
2026-03-18 00:30:01 -07:00
committed by GitHub
parent e6c6aaa11b
commit fbd88e2c8f
78 changed files with 476 additions and 327 deletions

View File

@@ -1,7 +1,8 @@
import { beforeEach, describe, expect, it, vi } from "vitest";
import type { PluginCompatibilityNotice } from "../plugins/status.js";
const readConfigFileSnapshot = vi.fn();
const buildPluginCompatibilityNotices = vi.fn(() => []);
const buildPluginCompatibilityNotices = vi.fn((): PluginCompatibilityNotice[] => []);
vi.mock("../config/config.js", () => ({
readConfigFileSnapshot,

View File

@@ -184,13 +184,13 @@ async function promptWebToolsConfig(
if (!entry) {
return false;
}
return hasExistingKey(nextConfig, provider as SP) || hasKeyInEnv(entry);
return hasExistingKey(nextConfig, provider) || hasKeyInEnv(entry);
};
const existingProvider: SP = (() => {
const stored = existingSearch?.provider;
if (stored && SEARCH_PROVIDER_OPTIONS.some((e) => e.value === stored)) {
return stored as SP;
return stored;
}
return (
SEARCH_PROVIDER_OPTIONS.find((e) => hasKeyForProvider(e.value))?.value ?? defaultProvider
@@ -242,8 +242,8 @@ async function promptWebToolsConfig(
nextSearch = { ...nextSearch, provider: providerChoice };
const entry = SEARCH_PROVIDER_OPTIONS.find((e) => e.value === providerChoice)!;
const existingKey = resolveExistingKey(nextConfig, providerChoice as SP);
const keyConfigured = hasExistingKey(nextConfig, providerChoice as SP);
const existingKey = resolveExistingKey(nextConfig, providerChoice);
const keyConfigured = hasExistingKey(nextConfig, providerChoice);
const envAvailable = entry.envKeys.some((k) => Boolean(process.env[k]?.trim()));
const envVarNames = entry.envKeys.join(" / ");
@@ -263,7 +263,7 @@ async function promptWebToolsConfig(
const key = String(keyInput ?? "").trim();
if (key || existingKey) {
const applied = applySearchKey(nextConfig, providerChoice as SP, (key || existingKey)!);
const applied = applySearchKey(nextConfig, providerChoice, (key || existingKey)!);
nextSearch = { ...applied.tools?.web?.search };
} else if (keyConfigured || envAvailable) {
nextSearch = { ...nextSearch };

View File

@@ -359,6 +359,8 @@ describe("normalizeCompatibilityConfigValues", () => {
providers: {
google: {
apiKey: "existing-google-key",
baseUrl: "https://generativelanguage.googleapis.com",
models: [],
},
},
},

View File

@@ -474,6 +474,11 @@ export function normalizeCompatibilityConfigValues(cfg: OpenClawConfig): {
};
const normalizeLegacyNanoBananaSkill = () => {
type ModelProviderEntry = Partial<
NonNullable<NonNullable<OpenClawConfig["models"]>["providers"]>[string]
>;
type ModelsConfigPatch = Partial<NonNullable<OpenClawConfig["models"]>>;
const rawSkills = next.skills;
if (!isRecord(rawSkills)) {
return;
@@ -544,14 +549,20 @@ export function normalizeCompatibilityConfigValues(cfg: OpenClawConfig): {
? structuredClone(rawLegacyEntry.apiKey)
: undefined);
const rawModels = isRecord(next.models) ? structuredClone(next.models) : {};
const rawProviders = isRecord(rawModels.providers) ? { ...rawModels.providers } : {};
const rawGoogle = isRecord(rawProviders.google) ? { ...rawProviders.google } : {};
const rawModels = (
isRecord(next.models) ? structuredClone(next.models) : {}
) as ModelsConfigPatch;
const rawProviders = (
isRecord(rawModels.providers) ? { ...rawModels.providers } : {}
) as Record<string, ModelProviderEntry>;
const rawGoogle = (
isRecord(rawProviders.google) ? { ...rawProviders.google } : {}
) as ModelProviderEntry;
const hasGoogleApiKey = rawGoogle.apiKey !== undefined;
if (!hasGoogleApiKey && legacyApiKey) {
rawGoogle.apiKey = legacyApiKey;
rawProviders.google = rawGoogle;
rawModels.providers = rawProviders;
rawModels.providers = rawProviders as NonNullable<OpenClawConfig["models"]>["providers"];
next = {
...next,
models: rawModels as OpenClawConfig["models"],