mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-03 02:20:22 +00:00
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:
@@ -17,7 +17,17 @@ function stubImageGenerationProviders() {
|
||||
id: "openai",
|
||||
defaultModel: "gpt-image-1",
|
||||
models: ["gpt-image-1"],
|
||||
supportedSizes: ["1024x1024"],
|
||||
capabilities: {
|
||||
generate: {
|
||||
supportsSize: true,
|
||||
},
|
||||
edit: {
|
||||
enabled: false,
|
||||
},
|
||||
geometry: {
|
||||
sizes: ["1024x1024"],
|
||||
},
|
||||
},
|
||||
generateImage: vi.fn(async () => {
|
||||
throw new Error("not used");
|
||||
}),
|
||||
|
||||
@@ -18,7 +18,7 @@ describe("extra-params: Google thinking payload compatibility", () => {
|
||||
api: "google-generative-ai",
|
||||
provider: "google",
|
||||
id: "gemini-3.1-pro-preview",
|
||||
} as Model<"openai-completions">,
|
||||
} as unknown as Model<"openai-completions">,
|
||||
thinkingLevel: "high",
|
||||
payload: {
|
||||
contents: [],
|
||||
|
||||
@@ -457,7 +457,7 @@ describe("createOpenClawCodingTools", () => {
|
||||
it("applies xai model compat for direct Grok tool cleanup", () => {
|
||||
const xaiTools = createOpenClawCodingTools({
|
||||
modelProvider: "xai",
|
||||
modelCompat: applyXaiModelCompat({}).compat,
|
||||
modelCompat: applyXaiModelCompat({ compat: {} }).compat,
|
||||
senderIsOwner: true,
|
||||
});
|
||||
|
||||
|
||||
@@ -18,10 +18,7 @@ function toolNames(tools: AnyAgentTool[]): string[] {
|
||||
|
||||
describe("applyModelProviderToolPolicy", () => {
|
||||
it("keeps web_search for non-xAI models", () => {
|
||||
const filtered = __testing.applyModelProviderToolPolicy(baseTools, {
|
||||
modelProvider: "openai",
|
||||
modelId: "gpt-4o-mini",
|
||||
});
|
||||
const filtered = __testing.applyModelProviderToolPolicy(baseTools);
|
||||
|
||||
expect(toolNames(filtered)).toEqual(["read", "web_search", "exec"]);
|
||||
});
|
||||
|
||||
@@ -392,10 +392,11 @@ describe("createImageGenerateTool", () => {
|
||||
throw new Error("expected image_generate tool");
|
||||
}
|
||||
|
||||
await expect(tool.execute("call-bad-aspect", { prompt: "portrait", aspectRatio: "7:5" }))
|
||||
.rejects.toThrow(
|
||||
"aspectRatio must be one of 1:1, 2:3, 3:2, 3:4, 4:3, 4:5, 5:4, 9:16, 16:9, or 21:9",
|
||||
);
|
||||
await expect(
|
||||
tool.execute("call-bad-aspect", { prompt: "portrait", aspectRatio: "7:5" }),
|
||||
).rejects.toThrow(
|
||||
"aspectRatio must be one of 1:1, 2:3, 3:2, 3:4, 4:3, 4:5, 5:4, 9:16, 16:9, or 21:9",
|
||||
);
|
||||
});
|
||||
|
||||
it("lists registered provider and model options", async () => {
|
||||
|
||||
@@ -230,7 +230,9 @@ function normalizeReferenceImages(args: Record<string, unknown>): string[] {
|
||||
return normalized;
|
||||
}
|
||||
|
||||
function parseImageGenerationModelRef(raw: string | undefined): { provider: string; model: string } | null {
|
||||
function parseImageGenerationModelRef(
|
||||
raw: string | undefined,
|
||||
): { provider: string; model: string } | null {
|
||||
const trimmed = raw?.trim();
|
||||
if (!trimmed) {
|
||||
return null;
|
||||
@@ -258,7 +260,8 @@ function resolveSelectedImageGenerationProvider(params: {
|
||||
}
|
||||
return listRuntimeImageGenerationProviders({ config: params.config }).find(
|
||||
(provider) =>
|
||||
provider.id === selectedRef.provider || (provider.aliases ?? []).includes(selectedRef.provider),
|
||||
provider.id === selectedRef.provider ||
|
||||
(provider.aliases ?? []).includes(selectedRef.provider),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -298,7 +301,9 @@ function validateImageGenerationCapabilities(params: {
|
||||
|
||||
if (params.size) {
|
||||
if (!modeCaps.supportsSize) {
|
||||
throw new ToolInputError(`${provider.id} ${isEdit ? "edit" : "generate"} does not support size overrides.`);
|
||||
throw new ToolInputError(
|
||||
`${provider.id} ${isEdit ? "edit" : "generate"} does not support size overrides.`,
|
||||
);
|
||||
}
|
||||
if ((geometry?.sizes?.length ?? 0) > 0 && !geometry?.sizes?.includes(params.size)) {
|
||||
throw new ToolInputError(
|
||||
@@ -309,7 +314,9 @@ function validateImageGenerationCapabilities(params: {
|
||||
|
||||
if (params.aspectRatio) {
|
||||
if (!modeCaps.supportsAspectRatio) {
|
||||
throw new ToolInputError(`${provider.id} ${isEdit ? "edit" : "generate"} does not support aspectRatio overrides.`);
|
||||
throw new ToolInputError(
|
||||
`${provider.id} ${isEdit ? "edit" : "generate"} does not support aspectRatio overrides.`,
|
||||
);
|
||||
}
|
||||
if (
|
||||
(geometry?.aspectRatios?.length ?? 0) > 0 &&
|
||||
@@ -323,7 +330,9 @@ function validateImageGenerationCapabilities(params: {
|
||||
|
||||
if (params.resolution) {
|
||||
if (!modeCaps.supportsResolution) {
|
||||
throw new ToolInputError(`${provider.id} ${isEdit ? "edit" : "generate"} does not support resolution overrides.`);
|
||||
throw new ToolInputError(
|
||||
`${provider.id} ${isEdit ? "edit" : "generate"} does not support resolution overrides.`,
|
||||
);
|
||||
}
|
||||
if (
|
||||
(geometry?.resolutions?.length ?? 0) > 0 &&
|
||||
|
||||
@@ -26,7 +26,7 @@ type AssistantLikeMessage = {
|
||||
};
|
||||
|
||||
function resolveLiveXaiModel() {
|
||||
return getModel("xai", "grok-4-1-fast-reasoning") ?? getModel("xai", "grok-4");
|
||||
return getModel("xai", "grok-4");
|
||||
}
|
||||
|
||||
async function collectDoneMessage(
|
||||
|
||||
@@ -722,7 +722,14 @@ export function createAccountScopedGroupAccessSection<TResolved>(params: {
|
||||
};
|
||||
}
|
||||
|
||||
type AccountScopedChannel = "discord" | "slack" | "telegram" | "imessage" | "signal";
|
||||
type AccountScopedChannel =
|
||||
| "bluebubbles"
|
||||
| "discord"
|
||||
| "imessage"
|
||||
| "line"
|
||||
| "signal"
|
||||
| "slack"
|
||||
| "telegram";
|
||||
type LegacyDmChannel = "discord" | "slack";
|
||||
|
||||
export function patchLegacyDmChannelConfig(params: {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 };
|
||||
|
||||
@@ -359,6 +359,8 @@ describe("normalizeCompatibilityConfigValues", () => {
|
||||
providers: {
|
||||
google: {
|
||||
apiKey: "existing-google-key",
|
||||
baseUrl: "https://generativelanguage.googleapis.com",
|
||||
models: [],
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -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"],
|
||||
|
||||
@@ -444,6 +444,14 @@ export type MemorySearchConfig = {
|
||||
};
|
||||
};
|
||||
|
||||
type WebSearchLegacyProviderConfig = {
|
||||
apiKey?: SecretInput;
|
||||
baseUrl?: string;
|
||||
model?: string;
|
||||
mode?: string;
|
||||
inlineCitations?: boolean;
|
||||
};
|
||||
|
||||
export type ToolsConfig = {
|
||||
/** Base tool profile applied before allow/deny lists. */
|
||||
profile?: ToolProfileId;
|
||||
@@ -465,6 +473,20 @@ export type ToolsConfig = {
|
||||
timeoutSeconds?: number;
|
||||
/** Cache TTL in minutes for search results. */
|
||||
cacheTtlMinutes?: number;
|
||||
/** @deprecated Legacy Brave credential path. */
|
||||
apiKey?: SecretInput;
|
||||
/** @deprecated Legacy Brave scoped config. */
|
||||
brave?: WebSearchLegacyProviderConfig;
|
||||
/** @deprecated Legacy Firecrawl scoped config. */
|
||||
firecrawl?: WebSearchLegacyProviderConfig;
|
||||
/** @deprecated Legacy Gemini scoped config. */
|
||||
gemini?: WebSearchLegacyProviderConfig;
|
||||
/** @deprecated Legacy Grok scoped config. */
|
||||
grok?: WebSearchLegacyProviderConfig;
|
||||
/** @deprecated Legacy Kimi scoped config. */
|
||||
kimi?: WebSearchLegacyProviderConfig;
|
||||
/** @deprecated Legacy Perplexity scoped config. */
|
||||
perplexity?: WebSearchLegacyProviderConfig;
|
||||
};
|
||||
fetch?: {
|
||||
/** Enable web fetch tool (default: true). */
|
||||
|
||||
@@ -267,6 +267,57 @@ export const ToolsWebSearchSchema = z
|
||||
maxResults: z.number().int().positive().optional(),
|
||||
timeoutSeconds: z.number().int().positive().optional(),
|
||||
cacheTtlMinutes: z.number().nonnegative().optional(),
|
||||
apiKey: SecretInputSchema.optional().register(sensitive),
|
||||
brave: z
|
||||
.object({
|
||||
apiKey: SecretInputSchema.optional().register(sensitive),
|
||||
baseUrl: z.string().optional(),
|
||||
model: z.string().optional(),
|
||||
mode: z.string().optional(),
|
||||
})
|
||||
.strict()
|
||||
.optional(),
|
||||
firecrawl: z
|
||||
.object({
|
||||
apiKey: SecretInputSchema.optional().register(sensitive),
|
||||
baseUrl: z.string().optional(),
|
||||
model: z.string().optional(),
|
||||
})
|
||||
.strict()
|
||||
.optional(),
|
||||
gemini: z
|
||||
.object({
|
||||
apiKey: SecretInputSchema.optional().register(sensitive),
|
||||
baseUrl: z.string().optional(),
|
||||
model: z.string().optional(),
|
||||
})
|
||||
.strict()
|
||||
.optional(),
|
||||
grok: z
|
||||
.object({
|
||||
apiKey: SecretInputSchema.optional().register(sensitive),
|
||||
baseUrl: z.string().optional(),
|
||||
model: z.string().optional(),
|
||||
inlineCitations: z.boolean().optional(),
|
||||
})
|
||||
.strict()
|
||||
.optional(),
|
||||
kimi: z
|
||||
.object({
|
||||
apiKey: SecretInputSchema.optional().register(sensitive),
|
||||
baseUrl: z.string().optional(),
|
||||
model: z.string().optional(),
|
||||
})
|
||||
.strict()
|
||||
.optional(),
|
||||
perplexity: z
|
||||
.object({
|
||||
apiKey: SecretInputSchema.optional().register(sensitive),
|
||||
baseUrl: z.string().optional(),
|
||||
model: z.string().optional(),
|
||||
})
|
||||
.strict()
|
||||
.optional(),
|
||||
})
|
||||
.strict()
|
||||
.optional();
|
||||
|
||||
@@ -94,14 +94,22 @@ function aspectRatioToEnum(aspectRatio: string | undefined): string | undefined
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function aspectRatioToDimensions(aspectRatio: string, edge: number): { width: number; height: number } {
|
||||
function aspectRatioToDimensions(
|
||||
aspectRatio: string,
|
||||
edge: number,
|
||||
): { width: number; height: number } {
|
||||
const match = /^(\d+):(\d+)$/u.exec(aspectRatio.trim());
|
||||
if (!match) {
|
||||
throw new Error(`Invalid fal aspect ratio: ${aspectRatio}`);
|
||||
}
|
||||
const widthRatio = Number.parseInt(match[1] ?? "", 10);
|
||||
const heightRatio = Number.parseInt(match[2] ?? "", 10);
|
||||
if (!Number.isFinite(widthRatio) || !Number.isFinite(heightRatio) || widthRatio <= 0 || heightRatio <= 0) {
|
||||
if (
|
||||
!Number.isFinite(widthRatio) ||
|
||||
!Number.isFinite(heightRatio) ||
|
||||
widthRatio <= 0 ||
|
||||
heightRatio <= 0
|
||||
) {
|
||||
throw new Error(`Invalid fal aspect ratio: ${aspectRatio}`);
|
||||
}
|
||||
if (widthRatio >= heightRatio) {
|
||||
@@ -140,7 +148,10 @@ function resolveFalImageSize(params: {
|
||||
return { width: edge, height: edge };
|
||||
}
|
||||
if (normalizedAspectRatio) {
|
||||
return aspectRatioToEnum(normalizedAspectRatio) ?? aspectRatioToDimensions(normalizedAspectRatio, 1024);
|
||||
return (
|
||||
aspectRatioToEnum(normalizedAspectRatio) ??
|
||||
aspectRatioToDimensions(normalizedAspectRatio, 1024)
|
||||
);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ describe("resolveOutboundSessionRoute", () => {
|
||||
from?: string;
|
||||
to?: string;
|
||||
threadId?: string | number;
|
||||
chatType?: "direct" | "group";
|
||||
chatType?: "channel" | "direct" | "group";
|
||||
};
|
||||
}> = [
|
||||
{
|
||||
|
||||
@@ -972,7 +972,7 @@ describe("resolveOutboundSessionRoute", () => {
|
||||
from?: string;
|
||||
to?: string;
|
||||
threadId?: string | number;
|
||||
chatType?: "direct" | "group";
|
||||
chatType?: "channel" | "direct" | "group";
|
||||
};
|
||||
}> = [
|
||||
{
|
||||
|
||||
@@ -1,6 +1,18 @@
|
||||
// Public ACP runtime helpers for plugins that integrate with ACP control/session state.
|
||||
|
||||
export { getAcpSessionManager } from "../acp/control-plane/manager.js";
|
||||
export { isAcpRuntimeError } from "../acp/runtime/errors.js";
|
||||
export { AcpRuntimeError, isAcpRuntimeError } from "../acp/runtime/errors.js";
|
||||
export type { AcpRuntimeErrorCode } from "../acp/runtime/errors.js";
|
||||
export type {
|
||||
AcpRuntime,
|
||||
AcpRuntimeCapabilities,
|
||||
AcpRuntimeDoctorReport,
|
||||
AcpRuntimeEnsureInput,
|
||||
AcpRuntimeEvent,
|
||||
AcpRuntimeHandle,
|
||||
AcpRuntimeStatus,
|
||||
AcpRuntimeTurnInput,
|
||||
AcpSessionUpdateTag,
|
||||
} from "../acp/runtime/types.js";
|
||||
export { readAcpSessionEntry } from "../acp/runtime/session-meta.js";
|
||||
export type { AcpSessionStoreEntry } from "../acp/runtime/session-meta.js";
|
||||
|
||||
@@ -41,8 +41,11 @@ export function resolveOptionalConfigString(
|
||||
}
|
||||
|
||||
/** Build the shared allowlist/default target adapter surface for account-scoped channel configs. */
|
||||
export function createScopedAccountConfigAccessors<ResolvedAccount>(params: {
|
||||
resolveAccount: (params: { cfg: OpenClawConfig; accountId?: string | null }) => ResolvedAccount;
|
||||
export function createScopedAccountConfigAccessors<
|
||||
ResolvedAccount,
|
||||
Config extends OpenClawConfig = OpenClawConfig,
|
||||
>(params: {
|
||||
resolveAccount: (params: { cfg: Config; accountId?: string | null }) => ResolvedAccount;
|
||||
resolveAllowFrom: (account: ResolvedAccount) => Array<string | number> | null | undefined;
|
||||
formatAllowFrom: (allowFrom: Array<string | number>) => string[];
|
||||
resolveDefaultTo?: (account: ResolvedAccount) => string | number | null | undefined;
|
||||
@@ -52,7 +55,9 @@ export function createScopedAccountConfigAccessors<ResolvedAccount>(params: {
|
||||
> {
|
||||
const base = {
|
||||
resolveAllowFrom: ({ cfg, accountId }: { cfg: OpenClawConfig; accountId?: string | null }) =>
|
||||
mapAllowFromEntries(params.resolveAllowFrom(params.resolveAccount({ cfg, accountId }))),
|
||||
mapAllowFromEntries(
|
||||
params.resolveAllowFrom(params.resolveAccount({ cfg: cfg as Config, accountId })),
|
||||
),
|
||||
formatAllowFrom: ({ allowFrom }: { allowFrom: Array<string | number> }) =>
|
||||
params.formatAllowFrom(allowFrom),
|
||||
};
|
||||
@@ -65,7 +70,7 @@ export function createScopedAccountConfigAccessors<ResolvedAccount>(params: {
|
||||
...base,
|
||||
resolveDefaultTo: ({ cfg, accountId }) =>
|
||||
resolveOptionalConfigString(
|
||||
params.resolveDefaultTo?.(params.resolveAccount({ cfg, accountId })),
|
||||
params.resolveDefaultTo?.(params.resolveAccount({ cfg: cfg as Config, accountId })),
|
||||
),
|
||||
};
|
||||
}
|
||||
@@ -160,7 +165,7 @@ export function createScopedChannelConfigAdapter<
|
||||
clearBaseFields: params.clearBaseFields,
|
||||
allowTopLevel: params.allowTopLevel,
|
||||
}),
|
||||
...createScopedAccountConfigAccessors<AccessorAccount>({
|
||||
...createScopedAccountConfigAccessors<AccessorAccount, Config>({
|
||||
resolveAccount: resolveAccessorAccount,
|
||||
resolveAllowFrom: params.resolveAllowFrom,
|
||||
formatAllowFrom: params.formatAllowFrom,
|
||||
@@ -316,7 +321,7 @@ export function createTopLevelChannelConfigAdapter<
|
||||
deleteMode: params.deleteMode,
|
||||
clearBaseFields: params.clearBaseFields,
|
||||
}),
|
||||
...createScopedAccountConfigAccessors<AccessorAccount>({
|
||||
...createScopedAccountConfigAccessors<AccessorAccount, Config>({
|
||||
resolveAccount: resolveAccessorAccount,
|
||||
resolveAllowFrom: params.resolveAllowFrom,
|
||||
formatAllowFrom: params.formatAllowFrom,
|
||||
@@ -438,7 +443,7 @@ export function createHybridChannelConfigAdapter<
|
||||
clearBaseFields: params.clearBaseFields,
|
||||
preserveSectionOnDefaultDelete: params.preserveSectionOnDefaultDelete,
|
||||
}),
|
||||
...createScopedAccountConfigAccessors<AccessorAccount>({
|
||||
...createScopedAccountConfigAccessors<AccessorAccount, Config>({
|
||||
resolveAccount: resolveAccessorAccount,
|
||||
resolveAllowFrom: params.resolveAllowFrom,
|
||||
formatAllowFrom: params.formatAllowFrom,
|
||||
|
||||
@@ -44,6 +44,7 @@ export type {
|
||||
ProviderThinkingPolicyContext,
|
||||
ProviderWrapStreamFnContext,
|
||||
OpenClawPluginService,
|
||||
OpenClawPluginServiceContext,
|
||||
ProviderAuthContext,
|
||||
ProviderAuthDoctorHintContext,
|
||||
ProviderAuthMethodNonInteractiveContext,
|
||||
@@ -51,6 +52,7 @@ export type {
|
||||
ProviderAuthResult,
|
||||
OpenClawPluginCommandDefinition,
|
||||
OpenClawPluginDefinition,
|
||||
PluginLogger,
|
||||
PluginInteractiveTelegramHandlerContext,
|
||||
} from "../plugins/types.js";
|
||||
export type { OpenClawConfig } from "../config/config.js";
|
||||
|
||||
@@ -25,7 +25,7 @@ function collectPluginSdkPackageExports(): string[] {
|
||||
}
|
||||
subpaths.push(key.slice("./plugin-sdk/".length));
|
||||
}
|
||||
return subpaths.sort();
|
||||
return subpaths.toSorted();
|
||||
}
|
||||
|
||||
function collectPluginSdkSourceNames(): string[] {
|
||||
@@ -35,7 +35,7 @@ function collectPluginSdkSourceNames(): string[] {
|
||||
(entry) => entry.isFile() && entry.name.endsWith(".ts") && !entry.name.endsWith(".test.ts"),
|
||||
)
|
||||
.map((entry) => entry.name.slice(0, -".ts".length))
|
||||
.sort();
|
||||
.toSorted();
|
||||
}
|
||||
|
||||
function collectTextFiles(rootRelativeDir: string): string[] {
|
||||
@@ -92,7 +92,7 @@ function collectPluginSdkSubpathReferences() {
|
||||
|
||||
describe("plugin-sdk package contract guardrails", () => {
|
||||
it("keeps package.json exports aligned with built plugin-sdk entrypoints", () => {
|
||||
expect(collectPluginSdkPackageExports()).toEqual([...pluginSdkEntrypoints].sort());
|
||||
expect(collectPluginSdkPackageExports()).toEqual([...pluginSdkEntrypoints].toSorted());
|
||||
});
|
||||
|
||||
it("keeps repo openclaw/plugin-sdk/<name> references on exported built subpaths", () => {
|
||||
@@ -135,7 +135,7 @@ describe("plugin-sdk package contract guardrails", () => {
|
||||
failures.push(
|
||||
`src/plugin-sdk/${sourceName}.ts is referenced as openclaw/plugin-sdk/${sourceName} in ${matchingRefs
|
||||
.map((reference) => reference.file)
|
||||
.sort()
|
||||
.toSorted()
|
||||
.join(", ")}, but ${sourceName} is not exported as a public plugin-sdk subpath`,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -26,6 +26,8 @@ export type { StickerMetadata } from "../../extensions/telegram/api.js";
|
||||
export { emptyPluginConfigSchema } from "../plugins/config-schema.js";
|
||||
export { DEFAULT_ACCOUNT_ID, normalizeAccountId } from "../routing/session-key.js";
|
||||
export { parseTelegramTopicConversation } from "../acp/conversation-id.js";
|
||||
export { clearAccountEntryFields } from "../channels/plugins/config-helpers.js";
|
||||
export { resolveTelegramPollVisibility } from "../poll-params.js";
|
||||
|
||||
export {
|
||||
PAIRING_APPROVED_MESSAGE,
|
||||
@@ -38,9 +40,6 @@ export {
|
||||
setAccountEnabledInConfigSection,
|
||||
} from "./channel-plugin-common.js";
|
||||
|
||||
export { clearAccountEntryFields } from "../channels/plugins/config-helpers.js";
|
||||
export { resolveTelegramPollVisibility } from "../poll-params.js";
|
||||
|
||||
export {
|
||||
projectCredentialSnapshotFields,
|
||||
resolveConfiguredFromCredentialStatuses,
|
||||
|
||||
@@ -99,6 +99,7 @@ describe("plugin shape compatibility matrix", () => {
|
||||
envVars: ["HYBRID_SEARCH_KEY"],
|
||||
placeholder: "hsk_...",
|
||||
signupUrl: "https://example.com/signup",
|
||||
credentialPath: "tools.web.search.hybrid-search.apiKey",
|
||||
getCredentialValue: () => "hsk-test",
|
||||
setCredentialValue(searchConfigTarget, value) {
|
||||
searchConfigTarget.apiKey = value;
|
||||
|
||||
@@ -68,7 +68,10 @@ function createProviderSecretRefConfig(
|
||||
}
|
||||
|
||||
function readProviderKey(config: OpenClawConfig, provider: ProviderUnderTest): unknown {
|
||||
return config.plugins?.entries?.[providerPluginId(provider)]?.config?.webSearch?.apiKey;
|
||||
const pluginConfig = config.plugins?.entries?.[providerPluginId(provider)]?.config as
|
||||
| { webSearch?: { apiKey?: unknown } }
|
||||
| undefined;
|
||||
return pluginConfig?.webSearch?.apiKey;
|
||||
}
|
||||
|
||||
function expectInactiveFirecrawlSecretRef(params: {
|
||||
|
||||
@@ -21,6 +21,7 @@ describe("web search runtime", () => {
|
||||
placeholder: "custom-...",
|
||||
signupUrl: "https://example.com/signup",
|
||||
autoDetectOrder: 1,
|
||||
credentialPath: "tools.web.search.custom.apiKey",
|
||||
getCredentialValue: () => "configured",
|
||||
setCredentialValue: () => {},
|
||||
createTool: () => ({
|
||||
|
||||
@@ -199,5 +199,6 @@ export async function runWebSearch(
|
||||
|
||||
export const __testing = {
|
||||
resolveSearchConfig,
|
||||
resolveSearchProvider: resolveWebSearchProviderId,
|
||||
resolveWebSearchProviderId,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user