refactor(deadcode): tighten extension root modeling (#108538)

This commit is contained in:
Peter Steinberger
2026-07-15 18:18:23 -07:00
committed by GitHub
parent 9c6c8446a7
commit d4183facf1
55 changed files with 412 additions and 189 deletions
+29 -5
View File
@@ -113,9 +113,9 @@ const rootBundledPluginRuntimeDependencies = [
"tokenjuice",
] as const;
function strictBundledPluginWorkspace() {
function strictBundledPluginWorkspace(extraEntries: readonly string[] = []) {
return {
entry: strictBundledPluginEntries,
entry: [...strictBundledPluginEntries, ...extraEntries],
project: ["*.ts!", "src/**/*.{js,mjs,ts}!"],
ignoreDependencies: bundledPluginIgnoredRuntimeDependencies,
} as const;
@@ -373,19 +373,41 @@ const config = {
project: ["index.js!", "scripts/**/*.js!"],
},
[`${BUNDLED_PLUGIN_ROOT_DIR}/amazon-bedrock-mantle`]: strictBundledPluginWorkspace(),
[`${BUNDLED_PLUGIN_ROOT_DIR}/amazon-bedrock`]: strictBundledPluginWorkspace(),
[`${BUNDLED_PLUGIN_ROOT_DIR}/acpx`]: strictBundledPluginWorkspace(),
[`${BUNDLED_PLUGIN_ROOT_DIR}/azure-speech`]: strictBundledPluginWorkspace(),
[`${BUNDLED_PLUGIN_ROOT_DIR}/browser`]: strictBundledPluginWorkspace([
// Core and plugin-SDK facades resolve these shipped Browser surfaces by basename.
"browser-control-auth.ts!",
"browser-config.ts!",
"browser-doctor.ts!",
"browser-host-inspection.ts!",
"browser-maintenance.ts!",
"browser-profiles.ts!",
]),
[`${BUNDLED_PLUGIN_ROOT_DIR}/cloudflare-ai-gateway`]: strictBundledPluginWorkspace(),
[`${BUNDLED_PLUGIN_ROOT_DIR}/chutes`]: strictBundledPluginWorkspace(),
[`${BUNDLED_PLUGIN_ROOT_DIR}/clawrouter`]: strictBundledPluginWorkspace(),
[`${BUNDLED_PLUGIN_ROOT_DIR}/cohere`]: strictBundledPluginWorkspace(),
[`${BUNDLED_PLUGIN_ROOT_DIR}/comfy`]: strictBundledPluginWorkspace(),
[`${BUNDLED_PLUGIN_ROOT_DIR}/copilot`]: strictBundledPluginWorkspace(),
[`${BUNDLED_PLUGIN_ROOT_DIR}/deepgram`]: strictBundledPluginWorkspace(),
[`${BUNDLED_PLUGIN_ROOT_DIR}/deepinfra`]: strictBundledPluginWorkspace(),
[`${BUNDLED_PLUGIN_ROOT_DIR}/discord`]: strictBundledPluginWorkspace(),
[`${BUNDLED_PLUGIN_ROOT_DIR}/elevenlabs`]: strictBundledPluginWorkspace(),
[`${BUNDLED_PLUGIN_ROOT_DIR}/featherless`]: strictBundledPluginWorkspace(),
[`${BUNDLED_PLUGIN_ROOT_DIR}/fal`]: strictBundledPluginWorkspace(),
[`${BUNDLED_PLUGIN_ROOT_DIR}/fireworks`]: strictBundledPluginWorkspace(),
[`${BUNDLED_PLUGIN_ROOT_DIR}/google`]: strictBundledPluginWorkspace(),
[`${BUNDLED_PLUGIN_ROOT_DIR}/huggingface`]: strictBundledPluginWorkspace(),
[`${BUNDLED_PLUGIN_ROOT_DIR}/kilocode`]: strictBundledPluginWorkspace(),
[`${BUNDLED_PLUGIN_ROOT_DIR}/kimi-coding`]: strictBundledPluginWorkspace(),
[`${BUNDLED_PLUGIN_ROOT_DIR}/microsoft`]: strictBundledPluginWorkspace(),
[`${BUNDLED_PLUGIN_ROOT_DIR}/memory-core`]: strictBundledPluginWorkspace(),
[`${BUNDLED_PLUGIN_ROOT_DIR}/memory-lancedb`]: strictBundledPluginWorkspace(),
[`${BUNDLED_PLUGIN_ROOT_DIR}/microsoft-foundry`]: strictBundledPluginWorkspace(),
[`${BUNDLED_PLUGIN_ROOT_DIR}/migrate-claude`]: strictBundledPluginWorkspace(),
[`${BUNDLED_PLUGIN_ROOT_DIR}/migrate-hermes`]: strictBundledPluginWorkspace(),
[`${BUNDLED_PLUGIN_ROOT_DIR}/minimax`]: strictBundledPluginWorkspace(),
[`${BUNDLED_PLUGIN_ROOT_DIR}/mistral`]: strictBundledPluginWorkspace(),
[`${BUNDLED_PLUGIN_ROOT_DIR}/moonshot`]: strictBundledPluginWorkspace(),
@@ -393,15 +415,17 @@ const config = {
[`${BUNDLED_PLUGIN_ROOT_DIR}/pixverse`]: strictBundledPluginWorkspace(),
[`${BUNDLED_PLUGIN_ROOT_DIR}/qianfan`]: strictBundledPluginWorkspace(),
[`${BUNDLED_PLUGIN_ROOT_DIR}/qwen`]: strictBundledPluginWorkspace(),
[`${BUNDLED_PLUGIN_ROOT_DIR}/qa-lab`]: strictBundledPluginWorkspace(),
[`${BUNDLED_PLUGIN_ROOT_DIR}/senseaudio`]: strictBundledPluginWorkspace(),
[`${BUNDLED_PLUGIN_ROOT_DIR}/tavily`]: strictBundledPluginWorkspace(),
[`${BUNDLED_PLUGIN_ROOT_DIR}/tencent`]: strictBundledPluginWorkspace(),
[`${BUNDLED_PLUGIN_ROOT_DIR}/vllm`]: strictBundledPluginWorkspace(),
[`${BUNDLED_PLUGIN_ROOT_DIR}/voyage`]: strictBundledPluginWorkspace(),
[`${BUNDLED_PLUGIN_ROOT_DIR}/xiaomi`]: strictBundledPluginWorkspace(),
[`${BUNDLED_PLUGIN_ROOT_DIR}/xai`]: strictBundledPluginWorkspace(),
[`${BUNDLED_PLUGIN_ROOT_DIR}/llama-cpp`]: {
entry: bundledPluginEntries,
project: ["index.ts!", "src/**/*.{js,mjs,ts}!"],
project: ["*.ts!", "src/**/*.{js,mjs,ts}!"],
ignoreDependencies: [
// The provider resolves node-llama-cpp from its own package at runtime
// so local embeddings use the plugin-owned native dependency.
@@ -416,14 +440,14 @@ const config = {
// public surface, so its exports are intentional even where the channel
// consumes only a subset.
entry: [...bundledPluginEntries, "protocol/index.ts!", "protocol/node.ts!"],
project: ["index.ts!", "src/**/*.{js,mjs,ts}!", "protocol/**/*.ts!"],
project: ["*.ts!", "src/**/*.{js,mjs,ts}!", "protocol/**/*.ts!"],
ignoreDependencies: bundledPluginIgnoredRuntimeDependencies,
},
[`${BUNDLED_PLUGIN_ROOT_DIR}/*`]: {
// Bundled plugins often load their public surface via string specifiers in
// `index.ts` contracts, so Knip needs these convention-based entry files.
entry: bundledPluginEntries,
project: ["index.ts!", "src/**/*.{js,mjs,ts}!"],
project: ["*.ts!", "src/**/*.{js,mjs,ts}!"],
ignoreDependencies: bundledPluginIgnoredRuntimeDependencies,
},
},
@@ -1,6 +1,7 @@
// Amazon Bedrock tests cover embedding provider plugin behavior.
import { describe, expect, it, vi } from "vitest";
import { testing, hasAwsCredentials } from "./embedding-provider.js";
import { hasAwsCredentials } from "./embedding-provider.js";
import { embeddingTesting as testing } from "./test-support.js";
describe("hasAwsCredentials", () => {
it("accepts static AWS key credentials without loading the credential chain", async () => {
@@ -288,12 +288,16 @@ function parseCohereBatch(family: Family, raw: string): number[][] {
return asNumberArrayBatch(embeddings);
}
export const testing = {
const testing = {
parseCohereBatch,
parseSingle,
stripInferenceProfilePrefix,
};
if (process.env.VITEST === "true") {
Reflect.set(globalThis, Symbol.for("openclaw.amazonBedrockEmbeddingTestApi"), testing);
}
// ---------------------------------------------------------------------------
// Provider
// ---------------------------------------------------------------------------
@@ -449,4 +453,3 @@ export async function hasAwsCredentials(
return false;
}
}
export { testing as __testing };
@@ -2,7 +2,8 @@
import { BedrockRuntimeClient, ConversationRole } from "@aws-sdk/client-bedrock-runtime";
import { onLlmRequestActivity } from "openclaw/plugin-sdk/provider-stream-shared";
import { afterEach, describe, expect, it, vi } from "vitest";
import { streamBedrock, streamSimpleBedrock, testing } from "./stream.runtime.js";
import { streamBedrock, streamSimpleBedrock } from "./stream.runtime.js";
import { streamTesting as testing } from "./test-support.js";
function bedrockModel(overrides: Record<string, unknown>) {
return {
+5 -1
View File
@@ -1173,7 +1173,7 @@ function createImageBlock(mimeType: string, data: string) {
}
/** Test-only hooks for Bedrock runtime conversion and endpoint policy. */
export const testing = {
const testing = {
buildAdditionalModelRequestFields,
convertMessages,
getConfiguredBedrockRegion,
@@ -1182,4 +1182,8 @@ export const testing = {
resolveSimpleBedrockOptions,
shouldUseExplicitBedrockEndpoint,
};
if (process.env.VITEST === "true") {
Reflect.set(globalThis, Symbol.for("openclaw.amazonBedrockStreamTestApi"), testing);
}
/* oxlint-disable max-lines -- TODO: split this grandfathered oversized file. */
+45
View File
@@ -0,0 +1,45 @@
type BedrockEmbeddingTestApi = {
parseCohereBatch: (family: string, raw: string) => number[][];
parseSingle: (family: string, raw: string) => number[];
stripInferenceProfilePrefix: (modelId: string) => string;
};
type BedrockStreamTestApi = {
buildAdditionalModelRequestFields: (
model: unknown,
options: Record<string, unknown>,
) => Record<string, unknown> | undefined;
convertMessages: (...args: unknown[]) => Array<{ content?: unknown; role?: unknown }>;
getConfiguredBedrockRegion: (params: unknown) => string | undefined;
hasConfiguredBedrockProfile: (params: unknown) => boolean;
mapThinkingLevelToEffort: (model: unknown, level: unknown) => string;
resolveSimpleBedrockOptions: (
model: unknown,
options: Record<string, unknown>,
) => Record<string, unknown>;
shouldUseExplicitBedrockEndpoint: (...args: unknown[]) => boolean;
};
function requireTestApi(key: string): object {
const api = Reflect.get(globalThis, Symbol.for(key));
if (!api) {
throw new Error(`${key} is unavailable`);
}
return api as object;
}
function lazyTestApi(key: string): object {
return new Proxy(
{},
{
get: (_target, property) => Reflect.get(requireTestApi(key), property),
},
);
}
export const embeddingTesting = lazyTestApi(
"openclaw.amazonBedrockEmbeddingTestApi",
) as BedrockEmbeddingTestApi;
export const streamTesting = lazyTestApi(
"openclaw.amazonBedrockStreamTestApi",
) as BedrockStreamTestApi;
+2 -2
View File
@@ -1,10 +1,10 @@
// Chutes tests cover models plugin behavior.
import { expectDefined } from "@openclaw/normalization-core";
import { clearLiveCatalogCacheForTests } from "openclaw/plugin-sdk/provider-catalog-live-runtime";
import { beforeEach, describe, expect, it, vi } from "vitest";
import {
buildChutesModelDefinition,
CHUTES_MODEL_CATALOG,
clearChutesModelCacheForTests,
discoverChutesModels,
} from "./models.js";
@@ -86,7 +86,7 @@ function requireChutesModel(
describe("chutes-models", () => {
beforeEach(() => {
clearChutesModelCacheForTests();
clearLiveCatalogCacheForTests();
});
it("buildChutesModelDefinition returns config with required fields", () => {
-6
View File
@@ -2,7 +2,6 @@
* Chutes model catalog, static model definitions, and dynamic model discovery.
*/
import {
clearLiveCatalogCacheForTests,
getCachedLiveProviderModelRows,
LiveModelCatalogHttpError,
} from "openclaw/plugin-sdk/provider-catalog-live-runtime";
@@ -489,11 +488,6 @@ interface ChutesModelEntry {
const CACHE_TTL = 5 * 60 * 1000;
/** Clears the dynamic Chutes model discovery cache for tests. */
export function clearChutesModelCacheForTests(): void {
clearLiveCatalogCacheForTests();
}
async function fetchChutesModelRows(accessToken?: string): Promise<readonly unknown[]> {
return await getCachedLiveProviderModelRows({
providerId: "chutes",
+5 -1
View File
@@ -2,7 +2,7 @@ import { createServer, type Server } from "node:http";
import { connect, type AddressInfo } from "node:net";
import type { Duplex } from "node:stream";
import { afterEach, beforeEach, describe, expect, it, vi, type MockedFunction } from "vitest";
import { fetchClawRouterUsage, type ClawRouterUsageFetchGuard } from "./usage.js";
import { fetchClawRouterUsage } from "./usage.js";
const runningServers: Server[] = [];
const runningSockets = new Set<Duplex>();
@@ -107,6 +107,10 @@ afterEach(async () => {
savedProxyEnv.clear();
});
type ClawRouterUsageFetchGuard = NonNullable<
Parameters<typeof fetchClawRouterUsage>[0]["fetchGuard"]
>;
function mockFetchGuard(response: Response): MockedFunction<ClawRouterUsageFetchGuard> {
return vi.fn(async ({ url }) => ({
response,
+1 -1
View File
@@ -9,7 +9,7 @@ import { normalizeClawRouterRootUrl } from "./provider-catalog.js";
const CLAWROUTER_USAGE_RESPONSE_MAX_BYTES = 1024 * 1024;
export type ClawRouterUsageFetchGuard = typeof fetchWithSsrFGuard;
type ClawRouterUsageFetchGuard = typeof fetchWithSsrFGuard;
type ClawRouterBudget = {
configured?: unknown;
+3 -2
View File
@@ -6,7 +6,8 @@ import { getRuntimeConfig } from "openclaw/plugin-sdk/runtime-config-snapshot";
import { isLiveTestEnabled } from "openclaw/plugin-sdk/test-env";
import { beforeAll, describe, expect, it } from "vitest";
import plugin from "./index.js";
import { getComfyConfig, isComfyCapabilityConfigured } from "./workflow-runtime.js";
import { getComfyConfigForTesting } from "./test-support.js";
import { isComfyCapabilityConfigured } from "./workflow-runtime.js";
const LIVE =
isLiveTestEnabled(["COMFY_LIVE_TEST"]) && (process.env.COMFY_LIVE_TEST ?? "").trim() === "1";
@@ -123,7 +124,7 @@ describeLive("comfy live", () => {
);
it("documents the effective comfy config shape for live debugging", () => {
const comfyConfig = getComfyConfig(cfg as never);
const comfyConfig = getComfyConfigForTesting(cfg as never);
expect(typeof comfyConfig).toBe("object");
});
});
@@ -3,10 +3,7 @@ import type { LookupAddress } from "node:dns";
import { MAX_TIMER_TIMEOUT_MS } from "openclaw/plugin-sdk/number-runtime";
import { fetchWithSsrFGuard } from "openclaw/plugin-sdk/ssrf-runtime";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import {
setComfyFetchGuardForTesting,
buildComfyImageGenerationProvider,
} from "./image-generation-provider.js";
import { buildComfyImageGenerationProvider } from "./image-generation-provider.js";
import {
buildComfyConfig,
buildLegacyComfyConfig,
@@ -14,6 +11,7 @@ import {
mockComfyProviderApiKey,
parseComfyJsonBody,
} from "./test-helpers.js";
import { setComfyFetchGuardForTesting } from "./test-support.js";
const { fetchWithSsrFGuardMock } = vi.hoisted(() => ({
fetchWithSsrFGuardMock: vi.fn(),
@@ -5,13 +5,10 @@ import type {
} from "openclaw/plugin-sdk/image-generation";
import {
DEFAULT_COMFY_MODEL,
setComfyFetchGuardForTesting,
isComfyCapabilityConfigured,
runComfyWorkflow,
} from "./workflow-runtime.js";
export { setComfyFetchGuardForTesting };
export function buildComfyImageGenerationProvider(): ImageGenerationProvider {
return {
id: "comfy",
@@ -2,7 +2,7 @@
import { expectExplicitMusicGenerationCapabilities } from "openclaw/plugin-sdk/provider-test-contracts";
import { afterEach, describe, expect, it, vi } from "vitest";
import { buildComfyMusicGenerationProvider } from "./music-generation-provider.js";
import { setComfyFetchGuardForTesting } from "./workflow-runtime.js";
import { setComfyFetchGuardForTesting } from "./test-support.js";
const { fetchWithSsrFGuardMock } = vi.hoisted(() => ({
fetchWithSsrFGuardMock: vi.fn(),
+22
View File
@@ -0,0 +1,22 @@
import type { fetchWithSsrFGuard } from "openclaw/plugin-sdk/ssrf-runtime";
type ComfyTestApi = {
getConfig: (cfg?: unknown) => Record<string, unknown>;
setFetchGuard: (impl: typeof fetchWithSsrFGuard | null) => void;
};
function getComfyTestApi(): ComfyTestApi {
const api = Reflect.get(globalThis, Symbol.for("openclaw.comfyTestApi"));
if (!api) {
throw new Error("Comfy test API is unavailable");
}
return api as ComfyTestApi;
}
export function setComfyFetchGuardForTesting(impl: typeof fetchWithSsrFGuard | null): void {
getComfyTestApi().setFetchGuard(impl);
}
export function getComfyConfigForTesting(cfg?: unknown): Record<string, unknown> {
return getComfyTestApi().getConfig(cfg);
}
@@ -7,10 +7,8 @@ import {
mockComfyProviderApiKey,
parseComfyJsonBody,
} from "./test-helpers.js";
import {
setComfyFetchGuardForTesting,
buildComfyVideoGenerationProvider,
} from "./video-generation-provider.js";
import { setComfyFetchGuardForTesting } from "./test-support.js";
import { buildComfyVideoGenerationProvider } from "./video-generation-provider.js";
const { fetchWithSsrFGuardMock } = vi.hoisted(() => ({
fetchWithSsrFGuardMock: vi.fn(),
@@ -6,13 +6,10 @@ import type {
} from "openclaw/plugin-sdk/video-generation";
import {
DEFAULT_COMFY_MODEL,
setComfyFetchGuardForTesting,
isComfyCapabilityConfigured,
runComfyWorkflow,
} from "./workflow-runtime.js";
export { setComfyFetchGuardForTesting };
function toComfyInputImage(inputImage?: VideoGenerationSourceAsset) {
if (!inputImage) {
return undefined;
+2 -1
View File
@@ -1,6 +1,7 @@
// Comfy tests cover workflow-runtime bounded-read delegation.
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { readJsonResponseForTest, setComfyFetchGuardForTesting } from "./workflow-runtime.js";
import { setComfyFetchGuardForTesting } from "./test-support.js";
import { readJsonResponseForTest } from "./workflow-runtime.js";
describe("readJsonResponse bounded read (readProviderJsonResponse delegation)", () => {
const fetchMock = vi.fn();
+9 -2
View File
@@ -114,10 +114,17 @@ type ComfyWorkflowResult = {
let comfyFetchGuard = fetchWithSsrFGuard;
export function setComfyFetchGuardForTesting(impl: typeof fetchWithSsrFGuard | null): void {
function setComfyFetchGuardForTesting(impl: typeof fetchWithSsrFGuard | null): void {
comfyFetchGuard = impl ?? fetchWithSsrFGuard;
}
if (process.env.VITEST === "true") {
Reflect.set(globalThis, Symbol.for("openclaw.comfyTestApi"), {
getConfig: getComfyConfig,
setFetchGuard: setComfyFetchGuardForTesting,
});
}
function resolveComfyGeneratedOutputMaxBytes(params: {
cfg: OpenClawConfig;
capability: ComfyCapability;
@@ -140,7 +147,7 @@ function readConfigInteger(config: ComfyProviderConfig, key: string): number | u
return typeof value === "number" && Number.isInteger(value) && value > 0 ? value : undefined;
}
export function getComfyConfig(cfg?: OpenClawConfig): ComfyProviderConfig {
function getComfyConfig(cfg?: OpenClawConfig): ComfyProviderConfig {
const pluginConfig = cfg?.plugins?.entries?.comfy?.config;
if (isRecord(pluginConfig)) {
return pluginConfig;
+1 -2
View File
@@ -12,10 +12,9 @@ import {
} from "openclaw/plugin-sdk/hook-runtime";
import { createMockPluginRegistry } from "openclaw/plugin-sdk/plugin-test-runtime";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import type { CopilotClientPool } from "./harness.js";
import { createCopilotAgentHarness, type CopilotSessionBinding } from "./harness.js";
import type { resolvePoolAcquire } from "./src/attempt.js";
import type { PoolKey } from "./src/runtime.js";
import type { CopilotClientPool, PoolKey } from "./src/runtime.js";
const COPILOT_BYOK_PROVIDER_ERROR =
"[copilot-attempt] BYOK requires an OpenAI-compatible or Anthropic model api and a non-empty baseUrl";
-2
View File
@@ -31,8 +31,6 @@ import type {
PoolKey,
} from "./src/runtime.js";
export type { CopilotClientPool, CopilotClientPoolOptions };
const COPILOT_PROVIDER_IDS: ReadonlySet<string> = new Set(["github-copilot"]);
interface CreateCopilotAgentHarnessOptions {
+2 -1
View File
@@ -6,7 +6,8 @@ import { CopilotClient, approveAll } from "@github/copilot-sdk";
import type { AgentHarnessAttemptParams } from "openclaw/plugin-sdk/agent-harness-runtime";
import { isLiveTestEnabled } from "openclaw/plugin-sdk/test-env";
import { describe, expect, it, vi } from "vitest";
import { createCopilotAgentHarness, type CopilotClientPool } from "../harness.js";
import { createCopilotAgentHarness } from "../harness.js";
import type { CopilotClientPool } from "./runtime.js";
const liveToolState = vi.hoisted(() => ({
calls: [] as string[],
+11 -11
View File
@@ -3,14 +3,14 @@ import {
createCapturedPluginRegistration,
registerSingleProviderPlugin,
} from "openclaw/plugin-sdk/plugin-test-runtime";
import { clearLiveCatalogCacheForTests } from "openclaw/plugin-sdk/provider-catalog-live-runtime";
import type { ProviderCatalogContext } from "openclaw/plugin-sdk/provider-catalog-shared";
import { describe, expect, it, vi } from "vitest";
import deepinfraPlugin from "./index.js";
import {
DEEPINFRA_MODEL_CATALOG,
DEEPINFRA_MODELS_URL,
resetDeepInfraModelCacheForTest,
} from "./provider-models.js";
import { DEEPINFRA_MODEL_CATALOG } from "./provider-models.js";
const DEEPINFRA_MODELS_URL =
"https://api.deepinfra.com/v1/openai/models?sort_by=openclaw&filter=with_meta";
function buildSyntheticDeepInfraEntries(count: number) {
return Array.from({ length: count }, (_unused, index) => ({
@@ -83,7 +83,7 @@ async function withLiveDiscoveryTestEnv(
describe("deepinfra augmentModelCatalog", () => {
it("returns the discovered (static under VITEST) catalog when nothing is configured", async () => {
resetDeepInfraModelCacheForTest();
clearLiveCatalogCacheForTests();
const provider = await registerSingleProviderPlugin(deepinfraPlugin);
const entries = (await provider.augmentModelCatalog?.({ entries: [] } as never)) ?? [];
@@ -97,7 +97,7 @@ describe("deepinfra augmentModelCatalog", () => {
});
it("preserves configured entries and appends discovered entries that are not already configured", async () => {
resetDeepInfraModelCacheForTest();
clearLiveCatalogCacheForTests();
const provider = await registerSingleProviderPlugin(deepinfraPlugin);
const entries =
@@ -129,7 +129,7 @@ describe("deepinfra augmentModelCatalog", () => {
});
it("uses config-backed API keys to enable live model catalog augmentation", async () => {
resetDeepInfraModelCacheForTest();
clearLiveCatalogCacheForTests();
const mockFetch = vi
.fn()
.mockResolvedValue(jsonResponse({ data: [makeAgentModelEntry("config/live-model")] }));
@@ -157,7 +157,7 @@ describe("deepinfra augmentModelCatalog", () => {
});
it("still runs live discovery when ctx.entries includes custom DeepInfra rows", async () => {
resetDeepInfraModelCacheForTest();
clearLiveCatalogCacheForTests();
const mockFetch = vi
.fn()
.mockResolvedValue(jsonResponse({ data: [makeAgentModelEntry("custom/live-model")] }));
@@ -205,7 +205,7 @@ describe("deepinfra augmentModelCatalog", () => {
});
it("still fetches when ctx.entries has exactly the static catalog length (static-fallback case)", async () => {
resetDeepInfraModelCacheForTest();
clearLiveCatalogCacheForTests();
const provider = await registerSingleProviderPlugin(deepinfraPlugin);
const entries =
@@ -235,7 +235,7 @@ describe("deepinfra capability registration", () => {
});
it("uses profile-resolved API keys for live text catalog discovery", async () => {
resetDeepInfraModelCacheForTest();
clearLiveCatalogCacheForTests();
const mockFetch = vi.fn().mockResolvedValue(jsonResponse({ data: [makeAgentModelEntry()] }));
const captured = createCapturedPluginRegistration();
deepinfraPlugin.register(captured.api);
@@ -4,10 +4,7 @@ import {
describeImageWithModel,
} from "openclaw/plugin-sdk/media-understanding";
import { afterAll, describe, expect, it, vi } from "vitest";
import {
deepinfraMediaUnderstandingProvider,
transcribeDeepInfraAudio,
} from "./media-understanding-provider.js";
import { deepinfraMediaUnderstandingProvider } from "./media-understanding-provider.js";
const { transcribeOpenAiCompatibleAudioMock } = vi.hoisted(() => ({
transcribeOpenAiCompatibleAudioMock: vi.fn(async () => ({ text: "hello", model: "whisper" })),
@@ -41,7 +38,7 @@ describe("deepinfra media understanding provider", () => {
image: 45,
audio: 45,
},
transcribeAudio: transcribeDeepInfraAudio,
transcribeAudio: expect.any(Function),
describeImage: describeImageWithModel,
describeImages: describeImagesWithModel,
});
@@ -49,7 +46,7 @@ describe("deepinfra media understanding provider", () => {
it("routes audio transcription through the OpenAI-compatible DeepInfra endpoint", async () => {
const buffer = Buffer.from("audio");
const result = await transcribeDeepInfraAudio({
const result = await deepinfraMediaUnderstandingProvider.transcribeAudio!({
buffer,
fileName: "clip.mp3",
apiKey: "deepinfra-key",
@@ -21,7 +21,7 @@ function resolveDefault(
return first ?? fallback[0] ?? "";
}
export async function transcribeDeepInfraAudio(params: AudioTranscriptionRequest) {
async function transcribeDeepInfraAudio(params: AudioTranscriptionRequest) {
return await transcribeOpenAiCompatibleAudio({
...params,
provider: "deepinfra",
+2 -10
View File
@@ -9,12 +9,8 @@ import {
} from "openclaw/plugin-sdk/provider-onboard";
import { captureEnv } from "openclaw/plugin-sdk/test-env";
import { afterEach, describe, expect, it, vi } from "vitest";
import {
applyDeepInfraConfig,
DEEPINFRA_BASE_URL,
DEEPINFRA_DEFAULT_MODEL_REF,
} from "./onboard.js";
import { DEEPINFRA_DEFAULT_MODEL_ID } from "./provider-models.js";
import { applyDeepInfraConfig } from "./onboard.js";
import { DEEPINFRA_BASE_URL, DEEPINFRA_DEFAULT_MODEL_REF } from "./provider-models.js";
const { resolveEnvApiKey } = providerAuth;
@@ -29,10 +25,6 @@ describe("DeepInfra provider config", () => {
it("DEEPINFRA_DEFAULT_MODEL_REF includes provider prefix", () => {
expect(DEEPINFRA_DEFAULT_MODEL_REF).toBe("deepinfra/deepseek-ai/DeepSeek-V4-Flash");
});
it("DEEPINFRA_DEFAULT_MODEL_ID is deepseek-ai/DeepSeek-V4-Flash", () => {
expect(DEEPINFRA_DEFAULT_MODEL_ID).toBe("deepseek-ai/DeepSeek-V4-Flash");
});
});
describe("applyDeepInfraConfig", () => {
+1 -3
View File
@@ -3,9 +3,7 @@ import {
applyAgentDefaultModelPrimary,
type OpenClawConfig,
} from "openclaw/plugin-sdk/provider-onboard";
import { DEEPINFRA_BASE_URL, DEEPINFRA_DEFAULT_MODEL_REF } from "./provider-models.js";
export { DEEPINFRA_BASE_URL, DEEPINFRA_DEFAULT_MODEL_REF };
import { DEEPINFRA_DEFAULT_MODEL_REF } from "./provider-models.js";
export function applyDeepInfraConfig(
cfg: OpenClawConfig,
+5 -3
View File
@@ -1,3 +1,4 @@
import { clearLiveCatalogCacheForTests } from "openclaw/plugin-sdk/provider-catalog-live-runtime";
// Deepinfra tests cover provider models plugin behavior.
import { beforeEach, describe, expect, it, vi } from "vitest";
@@ -13,17 +14,18 @@ vi.mock("openclaw/plugin-sdk/provider-auth", async () => {
});
import {
DEEPINFRA_MODELS_URL,
DEEPINFRA_DEFAULT_MODEL_REF,
DEEPINFRA_MODEL_CATALOG,
discoverDeepInfraModels,
discoverDeepInfraSurfaces,
hasDeepInfraApiKey,
resetDeepInfraModelCacheForTest,
} from "./provider-models.js";
const DEEPINFRA_MODELS_URL =
"https://api.deepinfra.com/v1/openai/models?sort_by=openclaw&filter=with_meta";
beforeEach(() => {
resetDeepInfraModelCacheForTest();
clearLiveCatalogCacheForTests();
isProviderApiKeyConfiguredMock.mockReset();
isProviderApiKeyConfiguredMock.mockReturnValue(false);
});
+2 -7
View File
@@ -1,7 +1,6 @@
// Deepinfra provider module implements model/runtime integration.
import { isProviderApiKeyConfigured } from "openclaw/plugin-sdk/provider-auth";
import {
clearLiveCatalogCacheForTests,
getCachedLiveProviderModelRows,
LiveModelCatalogHttpError,
} from "openclaw/plugin-sdk/provider-catalog-live-runtime";
@@ -20,9 +19,9 @@ const DEEPINFRA_MANIFEST_PROVIDER = buildManifestModelProviderConfig({
});
export const DEEPINFRA_BASE_URL = DEEPINFRA_MANIFEST_PROVIDER.baseUrl;
export const DEEPINFRA_MODELS_URL = `${DEEPINFRA_BASE_URL}/models?sort_by=openclaw&filter=with_meta`;
const DEEPINFRA_MODELS_URL = `${DEEPINFRA_BASE_URL}/models?sort_by=openclaw&filter=with_meta`;
export const DEEPINFRA_DEFAULT_MODEL_ID = "deepseek-ai/DeepSeek-V4-Flash";
const DEEPINFRA_DEFAULT_MODEL_ID = "deepseek-ai/DeepSeek-V4-Flash";
export const DEEPINFRA_DEFAULT_MODEL_REF = `deepinfra/${DEEPINFRA_DEFAULT_MODEL_ID}`;
const DEEPINFRA_DEFAULT_CONTEXT_WINDOW = 128000;
@@ -97,10 +96,6 @@ interface DeepInfraDiscoveredCatalog {
live: boolean;
}
export function resetDeepInfraModelCacheForTest(): void {
clearLiveCatalogCacheForTests();
}
const SURFACE_FOR_TAG: Record<string, DeepInfraSurface> = {
chat: "chat",
vlm: "vlm",
@@ -1,6 +1,6 @@
import { clearLiveCatalogCacheForTests } from "openclaw/plugin-sdk/provider-catalog-live-runtime";
// Deepinfra tests cover surface model catalogs plugin behavior.
import { beforeEach, describe, expect, it, vi } from "vitest";
import { resetDeepInfraModelCacheForTest } from "./provider-models.js";
import {
listDeepInfraImageGenCatalog,
listDeepInfraVideoGenCatalog,
@@ -8,7 +8,7 @@ import {
} from "./surface-model-catalogs.js";
beforeEach(() => {
resetDeepInfraModelCacheForTest();
clearLiveCatalogCacheForTests();
});
function makeCtx(overrides: Partial<Parameters<typeof listDeepInfraImageGenCatalog>[0]> = {}) {
@@ -1,6 +1,5 @@
// Discord plugin module implements runtime api.threads behavior.
export {
testing as __testing,
testing,
autoBindSpawnedDiscordSubagent,
createNoopThreadBindingManager,
@@ -6,10 +6,8 @@ const { fetchWithSsrFGuardMock } = vi.hoisted(() => ({
fetchWithSsrFGuardMock: vi.fn(),
}));
import {
setFalFetchGuardForTesting,
buildFalImageGenerationProvider,
} from "./image-generation-provider.js";
import { buildFalImageGenerationProvider } from "./image-generation-provider.js";
import { setFalFetchGuardForTesting } from "./test-support.js";
function expectFalJsonPost(params: { call: number; url: string; body: Record<string, unknown> }) {
const request = fetchWithSsrFGuardMock.mock.calls[params.call - 1]?.[0];
+7 -1
View File
@@ -155,10 +155,16 @@ type FalNetworkPolicy = {
let falFetchGuard = fetchWithSsrFGuard;
export function setFalFetchGuardForTesting(impl: typeof fetchWithSsrFGuard | null): void {
function setFalFetchGuardForTesting(impl: typeof fetchWithSsrFGuard | null): void {
falFetchGuard = impl ?? fetchWithSsrFGuard;
}
if (process.env.VITEST === "true") {
const key = Symbol.for("openclaw.falTestApi");
const api = (Reflect.get(globalThis, key) as Record<string, unknown> | undefined) ?? {};
Reflect.set(globalThis, key, { ...api, setImageFetchGuard: setFalFetchGuardForTesting });
}
function matchesTrustedHostSuffix(hostname: string, trustedSuffix: string): boolean {
const normalizedHost = normalizeLowercaseStringOrEmpty(hostname);
const normalizedSuffix = normalizeLowercaseStringOrEmpty(trustedSuffix);
+22
View File
@@ -0,0 +1,22 @@
import type { fetchWithSsrFGuard } from "openclaw/plugin-sdk/ssrf-runtime";
type FalTestApi = {
setImageFetchGuard: (impl: typeof fetchWithSsrFGuard | null) => void;
setVideoFetchGuard: (impl: typeof fetchWithSsrFGuard | null) => void;
};
function getFalTestApi(): FalTestApi {
const api = Reflect.get(globalThis, Symbol.for("openclaw.falTestApi"));
if (!api) {
throw new Error("Fal test API is unavailable");
}
return api as FalTestApi;
}
export function setFalFetchGuardForTesting(impl: typeof fetchWithSsrFGuard | null): void {
getFalTestApi().setImageFetchGuard(impl);
}
export function setFalVideoFetchGuardForTesting(impl: typeof fetchWithSsrFGuard | null): void {
getFalTestApi().setVideoFetchGuard(impl);
}
@@ -4,10 +4,8 @@ import * as providerAuth from "openclaw/plugin-sdk/provider-auth-runtime";
import * as providerHttp from "openclaw/plugin-sdk/provider-http";
import { expectExplicitVideoGenerationCapabilities } from "openclaw/plugin-sdk/provider-test-contracts";
import { afterEach, describe, expect, it, vi } from "vitest";
import {
setFalVideoFetchGuardForTesting,
buildFalVideoGenerationProvider,
} from "./video-generation-provider.js";
import { setFalVideoFetchGuardForTesting } from "./test-support.js";
import { buildFalVideoGenerationProvider } from "./video-generation-provider.js";
function createMockRequestConfig() {
return {} as ReturnType<typeof providerHttp.resolveProviderHttpRequestConfig>["requestConfig"];
+7 -1
View File
@@ -99,10 +99,16 @@ type FalQueueResponse = {
let falFetchGuard = fetchWithSsrFGuard;
export function setFalVideoFetchGuardForTesting(impl: typeof fetchWithSsrFGuard | null): void {
function setFalVideoFetchGuardForTesting(impl: typeof fetchWithSsrFGuard | null): void {
falFetchGuard = impl ?? fetchWithSsrFGuard;
}
if (process.env.VITEST === "true") {
const key = Symbol.for("openclaw.falTestApi");
const api = (Reflect.get(globalThis, key) as Record<string, unknown> | undefined) ?? {};
Reflect.set(globalThis, key, { ...api, setVideoFetchGuard: setFalVideoFetchGuardForTesting });
}
function normalizeFalVideoUrl(value: unknown): string | undefined {
const normalized = normalizeOptionalString(value);
if (!normalized && value !== undefined && value !== null) {
+1 -1
View File
@@ -35,7 +35,7 @@ import memoryPlugin, {
shouldCapture,
testing,
} from "./index.js";
import { createLanceDbRuntimeLoader } from "./lancedb-runtime.js";
import { createLanceDbRuntimeLoader } from "./lancedb-runtime.test-support.js";
import { installTmpDirHarness } from "./test-helpers.js";
const OPENAI_API_KEY = process.env.OPENAI_API_KEY ?? "test-key";
@@ -0,0 +1,16 @@
type LanceDbModule = typeof import("@lancedb/lancedb");
type LanceDbRuntimeTestApi = {
createRuntimeLoader: (overrides?: {
platform?: NodeJS.Platform;
arch?: NodeJS.Architecture;
importBundled?: () => Promise<LanceDbModule>;
}) => { load: () => Promise<LanceDbModule> };
};
const api = Reflect.get(globalThis, Symbol.for("openclaw.memoryLanceDbRuntimeTestApi"));
if (!api) {
throw new Error("Memory LanceDB runtime test API is unavailable");
}
export const createLanceDbRuntimeLoader = (api as LanceDbRuntimeTestApi).createRuntimeLoader;
+8 -2
View File
@@ -1,7 +1,7 @@
// Memory Lancedb plugin module implements lancedb runtime behavior.
type LanceDbModule = typeof import("@lancedb/lancedb");
export type LanceDbRuntimeLogger = {
type LanceDbRuntimeLogger = {
info?: (message: string) => void;
warn?: (message: string) => void;
};
@@ -38,7 +38,7 @@ function buildUnsupportedNativePlatformMessage(params: {
].join(" ");
}
export function createLanceDbRuntimeLoader(overrides: Partial<LanceDbRuntimeLoaderDeps> = {}): {
function createLanceDbRuntimeLoader(overrides: Partial<LanceDbRuntimeLoaderDeps> = {}): {
load: (loggerInstance?: LanceDbRuntimeLogger) => Promise<LanceDbModule>;
} {
const deps: LanceDbRuntimeLoaderDeps = {
@@ -71,6 +71,12 @@ export function createLanceDbRuntimeLoader(overrides: Partial<LanceDbRuntimeLoad
};
}
if (process.env.VITEST === "true") {
Reflect.set(globalThis, Symbol.for("openclaw.memoryLanceDbRuntimeTestApi"), {
createRuntimeLoader: createLanceDbRuntimeLoader,
});
}
const defaultLoader = createLanceDbRuntimeLoader();
export async function loadLanceDbModule(logger?: LanceDbRuntimeLogger): Promise<LanceDbModule> {
+7 -1
View File
@@ -35,7 +35,7 @@ import {
resolveFoundryApi,
} from "./shared.js";
export function shouldTestFoundryTextConnection(params: {
function shouldTestFoundryTextConnection(params: {
modelId: string;
modelNameHint?: string | null;
}): boolean {
@@ -44,6 +44,12 @@ export function shouldTestFoundryTextConnection(params: {
);
}
if (process.env.VITEST === "true") {
const key = Symbol.for("openclaw.microsoftFoundryTestApi");
const api = (Reflect.get(globalThis, key) as Record<string, unknown> | undefined) ?? {};
Reflect.set(globalThis, key, { ...api, shouldTestFoundryTextConnection });
}
export const entraIdAuthMethod: ProviderAuthMethod = {
id: "entra-id",
label: "Entra ID (az login)",
+12 -8
View File
@@ -3,33 +3,37 @@ import type { StreamFn } from "openclaw/plugin-sdk/agent-core";
import type { OpenClawConfig } from "openclaw/plugin-sdk/config-contracts";
import { createTestPluginApi } from "openclaw/plugin-sdk/plugin-test-api";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { shouldTestFoundryTextConnection } from "./auth.js";
import { getAccessTokenResultAsync } from "./cli.js";
import plugin from "./index.js";
import {
buildFoundryConnectionTest,
isValidTenantIdentifier,
promptApiKeyEndpointAndModel,
promptEndpointAndModelManually,
selectFoundryDeployment,
} from "./onboard.js";
import { resetFoundryRuntimeAuthCaches } from "./runtime.js";
import {
COGNITIVE_SERVICES_RESOURCE,
FOUNDRY_ANTHROPIC_SCOPE,
buildFoundryAuthResult,
extractFoundryEndpoint,
formatFoundryApiLabel,
isAnthropicFoundryDeployment,
isFoundryMaiImageModel,
normalizeFoundryEndpoint,
requiresFoundryMaxCompletionTokens,
requiresFoundryEntraIdClaudeAuth,
supportsFoundryReasoningContent,
supportsFoundryReasoningEffort,
supportsFoundryImageInput,
usesFoundryResponsesByDefault,
} from "./shared.js";
import { microsoftFoundryTesting } from "./test-support.js";
const {
buildFoundryConnectionTest,
isAnthropicFoundryDeployment,
isValidTenantIdentifier,
resetFoundryRuntimeAuthCaches,
shouldTestFoundryTextConnection,
supportsFoundryImageInput,
supportsFoundryReasoningContent,
supportsFoundryReasoningEffort,
} = microsoftFoundryTesting;
const execFileMock = vi.hoisted(() => vi.fn());
const execFileSyncMock = vi.hoisted(() => vi.fn());
+8 -2
View File
@@ -426,7 +426,7 @@ export async function promptApiKeyEndpointAndModel(
});
}
export function buildFoundryConnectionTest(params: {
function buildFoundryConnectionTest(params: {
endpoint: string;
modelId: string;
modelNameHint?: string | null;
@@ -489,7 +489,7 @@ function extractTenantSuggestions(rawMessage: string): Array<{ id: string; label
return suggestions;
}
export function isValidTenantIdentifier(value: string): boolean {
function isValidTenantIdentifier(value: string): boolean {
const trimmed = normalizeOptionalString(value) ?? "";
if (!trimmed) {
return false;
@@ -503,6 +503,12 @@ export function isValidTenantIdentifier(value: string): boolean {
return isTenantUuid || isTenantDomain;
}
if (process.env.VITEST === "true") {
const key = Symbol.for("openclaw.microsoftFoundryTestApi");
const api = (Reflect.get(globalThis, key) as Record<string, unknown> | undefined) ?? {};
Reflect.set(globalThis, key, { ...api, buildFoundryConnectionTest, isValidTenantIdentifier });
}
export async function promptTenantId(
ctx: ProviderAuthContext,
params?: {
+7 -1
View File
@@ -28,11 +28,17 @@ const cachedTokens = new Map<string, CachedTokenEntry>();
const refreshPromises = new Map<string, Promise<{ apiKey: string; expiresAt: number }>>();
const FOUNDRY_TOKEN_FALLBACK_LIFETIME_MS = 55 * 60 * 1000;
export function resetFoundryRuntimeAuthCaches(): void {
function resetFoundryRuntimeAuthCaches(): void {
cachedTokens.clear();
refreshPromises.clear();
}
if (process.env.VITEST === "true") {
const key = Symbol.for("openclaw.microsoftFoundryTestApi");
const api = (Reflect.get(globalThis, key) as Record<string, unknown> | undefined) ?? {};
Reflect.set(globalThis, key, { ...api, resetFoundryRuntimeAuthCaches });
}
async function refreshEntraToken(params?: {
scope?: string;
subscriptionId?: string;
+16 -4
View File
@@ -144,7 +144,7 @@ function normalizeFoundryModelName(value?: string | null): string | undefined {
return trimmed || undefined;
}
export function isAnthropicFoundryDeployment(modelName?: string | null): boolean {
function isAnthropicFoundryDeployment(modelName?: string | null): boolean {
const normalized = normalizeFoundryModelName(modelName);
return normalized ? normalized.startsWith("claude") : false;
}
@@ -182,12 +182,12 @@ export function isFoundryMaiImageModel(value?: string | null): boolean {
);
}
export function supportsFoundryReasoningContent(value?: string | null): boolean {
function supportsFoundryReasoningContent(value?: string | null): boolean {
const normalized = normalizeFoundryModelName(value);
return normalized === "mai-ds-r1" || normalized === "mai-thinking-1";
}
export function supportsFoundryImageInput(value?: string | null): boolean {
function supportsFoundryImageInput(value?: string | null): boolean {
const normalized = normalizeFoundryModelName(value);
if (!normalized) {
return false;
@@ -267,7 +267,7 @@ export function requiresFoundryMaxCompletionTokens(value?: string | null): boole
);
}
export function supportsFoundryReasoningEffort(value?: string | null): boolean {
function supportsFoundryReasoningEffort(value?: string | null): boolean {
const normalized = normalizeFoundryModelName(value);
if (
!normalized ||
@@ -284,6 +284,18 @@ export function supportsFoundryReasoningEffort(value?: string | null): boolean {
);
}
if (process.env.VITEST === "true") {
const key = Symbol.for("openclaw.microsoftFoundryTestApi");
const api = (Reflect.get(globalThis, key) as Record<string, unknown> | undefined) ?? {};
Reflect.set(globalThis, key, {
...api,
isAnthropicFoundryDeployment,
supportsFoundryImageInput,
supportsFoundryReasoningContent,
supportsFoundryReasoningEffort,
});
}
function resolveFoundryReasoningEfforts(value?: string | null): string[] | undefined {
const normalized = normalizeFoundryModelName(value);
if (!normalized || !supportsFoundryReasoningEffort(normalized)) {
@@ -0,0 +1,27 @@
import type { FoundryProviderApi } from "./shared.js";
type MicrosoftFoundryTestApi = {
buildFoundryConnectionTest: (params: {
endpoint: string;
modelId: string;
modelNameHint?: string | null;
api: FoundryProviderApi;
}) => { url: string; body: Record<string, unknown> };
isAnthropicFoundryDeployment: (value?: string | null) => boolean;
isValidTenantIdentifier: (value: string) => boolean;
resetFoundryRuntimeAuthCaches: () => void;
shouldTestFoundryTextConnection: (params: {
modelId: string;
modelNameHint?: string | null;
}) => boolean;
supportsFoundryImageInput: (value?: string | null) => boolean;
supportsFoundryReasoningContent: (value?: string | null) => boolean;
supportsFoundryReasoningEffort: (value?: string | null) => boolean;
};
const api = Reflect.get(globalThis, Symbol.for("openclaw.microsoftFoundryTestApi"));
if (!api) {
throw new Error("Microsoft Foundry test API is unavailable");
}
export const microsoftFoundryTesting = api as MicrosoftFoundryTestApi;
+1 -1
View File
@@ -12,7 +12,7 @@ type ClaudeArchivePath = {
relativePath: string;
};
export type ClaudeAutoMemorySource = {
type ClaudeAutoMemorySource = {
id: string;
label: string;
path: string;
+1 -1
View File
@@ -10,7 +10,7 @@ export type HermesAuthProfileConfig = {
displayName?: string;
};
export type HermesAuthConfigApplyResult = "configured" | "conflict" | "unavailable";
type HermesAuthConfigApplyResult = "configured" | "conflict" | "unavailable";
class HermesAuthConfigConflict extends Error {}
@@ -18,7 +18,7 @@ import {
import { childRecord, isRecord, readString, sanitizeName } from "./helpers.js";
import { normalizeHermesCustomProviderId, resolveHermesConfiguredProviderId } from "./model.js";
export type HermesProviderSecretBinding = {
type HermesProviderSecretBinding = {
envVar: string;
provider: string;
};
@@ -7,7 +7,7 @@ import { MIGRATION_REASON_TARGET_EXISTS } from "openclaw/plugin-sdk/migration";
import { afterEach, describe, expect, it, vi } from "vitest";
import { buildAuthItems } from "./auth.js";
import { buildHermesMigrationProvider } from "./provider.js";
import { discoverHermesSource, resolveImplicitHermesRoot } from "./source.js";
import { discoverHermesSource } from "./source.js";
import { resolveTargets } from "./targets.js";
import { cleanupTempRoots, makeContext, makeTempRoot, writeFile } from "./test/provider-helpers.js";
@@ -95,14 +95,23 @@ describe("Hermes migration file and skill items", () => {
await writeFile(path.join(defaultRoot, "active_profile"), "coder\n");
await writeFile(path.join(profileRoot, "config.yaml"), "model: openai/gpt-5.6\n");
expect(await resolveImplicitHermesRoot({ HERMES_HOME: defaultRoot }, "darwin")).toBe(
defaultRoot,
);
expect(await resolveImplicitHermesRoot({ HOME: home }, "darwin")).toBe(profileRoot);
expect(
(
await discoverHermesSource(undefined, {
env: { HERMES_HOME: defaultRoot },
platform: "darwin",
})
).root,
).toBe(defaultRoot);
expect(
(await discoverHermesSource(undefined, { env: { HOME: home }, platform: "darwin" })).root,
).toBe(profileRoot);
expect((await discoverHermesSource(profileRoot)).root).toBe(profileRoot);
await writeFile(path.join(defaultRoot, "active_profile"), "../escape\n");
expect(await resolveImplicitHermesRoot({ HOME: home }, "darwin")).toBe(defaultRoot);
expect(
(await discoverHermesSource(undefined, { env: { HOME: home }, platform: "darwin" })).root,
).toBe(defaultRoot);
const windowsHome = path.join(root, "windows-home");
const localAppData = path.join(windowsHome, "AppData", "Local");
@@ -110,17 +119,21 @@ describe("Hermes migration file and skill items", () => {
const legacyWindowsRoot = path.join(windowsHome, ".hermes");
await writeFile(path.join(legacyWindowsRoot, "config.yaml"), "model: legacy\n");
expect(
await resolveImplicitHermesRoot(
{ LOCALAPPDATA: localAppData, USERPROFILE: windowsHome },
"win32",
),
(
await discoverHermesSource(undefined, {
env: { LOCALAPPDATA: localAppData, USERPROFILE: windowsHome },
platform: "win32",
})
).root,
).toBe(legacyWindowsRoot);
await writeFile(path.join(nativeWindowsRoot, "config.yaml"), "model: current\n");
expect(
await resolveImplicitHermesRoot(
{ LOCALAPPDATA: localAppData, USERPROFILE: windowsHome },
"win32",
),
(
await discoverHermesSource(undefined, {
env: { LOCALAPPDATA: localAppData, USERPROFILE: windowsHome },
platform: "win32",
})
).root,
).toBe(nativeWindowsRoot);
const personaHome = path.join(root, "persona-home");
@@ -128,10 +141,12 @@ describe("Hermes migration file and skill items", () => {
const personaLegacyRoot = path.join(personaHome, ".hermes");
await writeFile(path.join(personaLegacyRoot, "SOUL.md"), "Legacy persona\n");
expect(
await resolveImplicitHermesRoot(
{ LOCALAPPDATA: personaLocalAppData, USERPROFILE: personaHome },
"win32",
),
(
await discoverHermesSource(undefined, {
env: { LOCALAPPDATA: personaLocalAppData, USERPROFILE: personaHome },
platform: "win32",
})
).root,
).toBe(personaLegacyRoot);
});
+1 -1
View File
@@ -179,7 +179,7 @@ export async function discoverHermesSource(
};
}
export async function resolveImplicitHermesRoot(
async function resolveImplicitHermesRoot(
env: NodeJS.ProcessEnv,
platform: NodeJS.Platform,
): Promise<string> {
+1 -1
View File
@@ -7,7 +7,7 @@ import {
export type { QaProviderMode, QaProviderModeInput } from "./providers/index.js";
export type QaModelSelection = {
type QaModelSelection = {
primaryModel: string;
alternateModel: string;
};
+2 -1
View File
@@ -1,7 +1,8 @@
// Voyage batch tests cover bounded status/error response reads.
import { describe, expect, it } from "vitest";
import { runVoyageEmbeddingBatches, testing } from "./embedding-batch.js";
import { runVoyageEmbeddingBatches } from "./embedding-batch.js";
import type { VoyageEmbeddingClient } from "./embedding-provider.js";
import { voyageEmbeddingBatchTesting as testing } from "./test-support.js";
const { fetchVoyageBatchStatus, readVoyageBatchError, VOYAGE_BATCH_RESPONSE_MAX_BYTES } = testing;
+5 -1
View File
@@ -339,8 +339,12 @@ export async function runVoyageEmbeddingBatches(
});
}
export const testing = {
const testing = {
fetchVoyageBatchStatus,
readVoyageBatchError,
VOYAGE_BATCH_RESPONSE_MAX_BYTES,
} as const;
if (process.env.VITEST === "true") {
Reflect.set(globalThis, Symbol.for("openclaw.voyageEmbeddingBatchTestApi"), testing);
}
+22
View File
@@ -0,0 +1,22 @@
import type { VoyageEmbeddingClient } from "./embedding-provider.js";
type VoyageBatchTestParams = {
client: VoyageEmbeddingClient;
deps: Record<string, unknown>;
maxResponseBytes?: number;
};
type VoyageEmbeddingBatchTestApi = {
fetchVoyageBatchStatus: (params: VoyageBatchTestParams & { batchId: string }) => Promise<unknown>;
readVoyageBatchError: (
params: VoyageBatchTestParams & { errorFileId: string },
) => Promise<string | undefined>;
VOYAGE_BATCH_RESPONSE_MAX_BYTES: number;
};
const api = Reflect.get(globalThis, Symbol.for("openclaw.voyageEmbeddingBatchTestApi"));
if (!api) {
throw new Error("Voyage embedding batch test API is unavailable");
}
export const voyageEmbeddingBatchTesting = api as VoyageEmbeddingBatchTestApi;
+36 -46
View File
@@ -40,56 +40,46 @@ describe("check-deadcode-exports", () => {
expect(knipConfig.workspaces["."].entry).toContain("src/mcp/openclaw-tools-serve.ts!");
});
it.each([
"acpx",
"amazon-bedrock-mantle",
"azure-speech",
"cloudflare-ai-gateway",
"cohere",
"deepgram",
"elevenlabs",
"featherless",
"fireworks",
"google",
"huggingface",
"kilocode",
"kimi-coding",
"lmstudio",
"microsoft",
"minimax",
"mistral",
"moonshot",
"nvidia",
"pixverse",
"qianfan",
"qwen",
"senseaudio",
"tavily",
"tencent",
"vllm",
"xiaomi",
"xai",
])("removes the bundled-plugin root catch-all from migrated %s workspace", (pluginId) => {
const workspace = (
knipConfig.workspaces as Record<string, { readonly entry: readonly string[] }>
)[`extensions/${pluginId}`];
if (!workspace) {
throw new Error(`missing Knip workspace for ${pluginId}`);
it("keeps every migrated bundled-plugin workspace strict", () => {
const extensionWorkspaces = Object.entries(knipConfig.workspaces).filter(
([workspace]) =>
workspace.startsWith("extensions/") &&
!["extensions/*", "extensions/llama-cpp", "extensions/reef"].includes(workspace),
);
expect(extensionWorkspaces.length).toBeGreaterThan(1);
for (const [workspace, settings] of extensionWorkspaces) {
expect(settings.entry, workspace).not.toContain("*.ts!");
expect(settings.project, workspace).toContain("*.ts!");
expect(settings.entry, workspace).toEqual(
expect.arrayContaining([
"index.ts!",
"setup-entry.ts!",
"*-api.ts!",
"cli-metadata.ts!",
"channel-entry.ts!",
"provider-discovery.ts!",
"{web-search,web-fetch}-provider.ts!",
]),
);
}
const entries = workspace.entry;
expect(entries).not.toContain("*.ts!");
expect(entries).toEqual(
expect(knipConfig.workspaces["extensions/*"].entry).toContain("*.ts!");
expect(knipConfig.workspaces["extensions/*"].project).toContain("*.ts!");
expect(knipConfig.workspaces["extensions/llama-cpp"].entry).toContain("*.ts!");
expect(knipConfig.workspaces["extensions/reef"].entry).toContain("*.ts!");
});
it("models the Browser facades loaded by basename", () => {
const workspace = knipConfig.workspaces["extensions/browser"];
expect(workspace.entry).toEqual(
expect.arrayContaining([
"index.ts!",
"setup-entry.ts!",
"*-api.ts!",
"cli-metadata.ts!",
"channel-entry.ts!",
"provider-discovery.ts!",
"{web-search,web-fetch}-provider.ts!",
"browser-control-auth.ts!",
"browser-config.ts!",
"browser-doctor.ts!",
"browser-host-inspection.ts!",
"browser-maintenance.ts!",
"browser-profiles.ts!",
]),
);
expect(knipConfig.workspaces["extensions/*"].entry).toContain("*.ts!");
});
it.each([