Merge branch 'main' into fix/openclaw-serialize-panic

# Conflicts:
#	src-tauri/src/proxy/providers/claude.rs
#	src-tauri/src/services/stream_check.rs
This commit is contained in:
YoVinchen
2026-03-29 21:07:06 +08:00
53 changed files with 2816 additions and 689 deletions
@@ -65,4 +65,19 @@ describe("AWS Bedrock OpenCode Provider Presets", () => {
modelIds.some((id) => id.includes("anthropic.claude")),
).toBe(true);
});
it("Kimi For Coding preset should use Anthropic with the coding endpoint", () => {
const kimiForCodingPreset = opencodeProviderPresets.find(
(p) => p.name === "Kimi For Coding",
);
expect(kimiForCodingPreset).toBeDefined();
expect(kimiForCodingPreset!.settingsConfig.npm).toBe("@ai-sdk/anthropic");
expect(kimiForCodingPreset!.settingsConfig.options?.baseURL).toBe(
"https://api.kimi.com/coding/v1",
);
expect(kimiForCodingPreset!.templateValues?.baseURL.defaultValue).toBe(
"https://api.kimi.com/coding/v1",
);
});
});
+50
View File
@@ -7,11 +7,15 @@ import type { Provider, UsageScript } from "@/types";
const toastSuccessMock = vi.fn();
const toastErrorMock = vi.fn();
const toastInfoMock = vi.fn();
const toastWarningMock = vi.fn();
vi.mock("sonner", () => ({
toast: {
success: (...args: unknown[]) => toastSuccessMock(...args),
error: (...args: unknown[]) => toastErrorMock(...args),
info: (...args: unknown[]) => toastInfoMock(...args),
warning: (...args: unknown[]) => toastWarningMock(...args),
},
}));
@@ -116,6 +120,8 @@ beforeEach(() => {
openclawApiSetDefaultModelMock.mockReset();
toastSuccessMock.mockReset();
toastErrorMock.mockReset();
toastInfoMock.mockReset();
toastWarningMock.mockReset();
addProviderMutation.isPending = false;
updateProviderMutation.isPending = false;
@@ -185,6 +191,50 @@ describe("useProviderActions", () => {
expect(settingsApiApplyMock).not.toHaveBeenCalled();
});
it("blocks switching providers that require proxy when proxy is not running", async () => {
const { wrapper } = createWrapper();
const provider = createProvider({
category: "custom",
meta: {
apiFormat: "openai_chat",
},
});
const { result } = renderHook(() => useProviderActions("claude", false), {
wrapper,
});
await act(async () => {
await result.current.switchProvider(provider);
});
expect(switchProviderMutateAsync).not.toHaveBeenCalled();
expect(toastWarningMock).toHaveBeenCalledTimes(1);
expect(settingsApiGetMock).not.toHaveBeenCalled();
});
it("blocks switching Codex full URL providers when proxy is not running", async () => {
const { wrapper } = createWrapper();
const provider = createProvider({
category: "custom",
meta: {
isFullUrl: true,
},
});
const { result } = renderHook(() => useProviderActions("codex", false), {
wrapper,
});
await act(async () => {
await result.current.switchProvider(provider);
});
expect(switchProviderMutateAsync).not.toHaveBeenCalled();
expect(toastWarningMock).toHaveBeenCalledTimes(1);
expect(settingsApiGetMock).not.toHaveBeenCalled();
});
it("should sync plugin config when switching Claude provider with integration enabled", async () => {
switchProviderMutateAsync.mockResolvedValueOnce(undefined);
settingsApiGetMock.mockResolvedValueOnce({