From b4033fdd2902fbd1945b0c4f757626523f66e314 Mon Sep 17 00:00:00 2001 From: wavever Date: Tue, 10 Mar 2026 16:14:08 +0800 Subject: [PATCH 1/4] fix: add missing authHeader: true to Longcat provider preset (#1377) Fixes issue where Longcat models were failing with 404 error due to missing authHeader configuration. According to Longcat API documentation, this setting is required for proper authentication. Closes: #1376 --- src/config/openclawProviderPresets.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/config/openclawProviderPresets.ts b/src/config/openclawProviderPresets.ts index f9b5539f..d20b55ba 100644 --- a/src/config/openclawProviderPresets.ts +++ b/src/config/openclawProviderPresets.ts @@ -458,6 +458,7 @@ export const openclawProviderPresets: OpenClawProviderPreset[] = [ baseUrl: "https://api.longcat.chat/v1", apiKey: "", api: "openai-completions", + authHeader: true, models: [ { id: "LongCat-Flash-Chat", From 84668e23070299a39b7eb7765206ef6f7eb17a07 Mon Sep 17 00:00:00 2001 From: liuxxxu <2429875446@qq.com> Date: Tue, 10 Mar 2026 16:53:40 +0800 Subject: [PATCH 2/4] =?UTF-8?q?feat(openClaw=20form):=20add=20input=20type?= =?UTF-8?q?=20selection=20for=20models=20Advanced=20Options=20/=20?= =?UTF-8?q?=E4=B8=BA=E6=A8=A1=E5=9E=8B=E9=AB=98=E7=BA=A7=E9=80=89=E9=A1=B9?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E8=BE=93=E5=85=A5=E7=B1=BB=E5=9E=8B=E9=80=89?= =?UTF-8?q?=E6=8B=A9=20(#1368)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(openClaw form): add input type selection for models Advanced Options / 为模型高级选项添加输入类型选择 * fix(i18n): add missing openclaw.inputTypes key to all locales The new inputTypes field in OpenClawFormFields used defaultValue fallback, causing English and Japanese users to see Chinese text. --------- Co-authored-by: xu.liu2 Co-authored-by: Jason --- .../providers/forms/OpenClawFormFields.tsx | 88 +++++++++++++------ src/i18n/locales/en.json | 1 + src/i18n/locales/ja.json | 1 + src/i18n/locales/zh.json | 1 + 4 files changed, 66 insertions(+), 25 deletions(-) diff --git a/src/components/providers/forms/OpenClawFormFields.tsx b/src/components/providers/forms/OpenClawFormFields.tsx index 74568264..8c35e0df 100644 --- a/src/components/providers/forms/OpenClawFormFields.tsx +++ b/src/components/providers/forms/OpenClawFormFields.tsx @@ -17,6 +17,7 @@ import { CollapsibleTrigger, } from "@/components/ui/collapsible"; import { Plus, Trash2, ChevronDown, ChevronRight } from "lucide-react"; +import { Checkbox } from "@/components/ui/checkbox"; import { ApiKeySection } from "./shared"; import { openclawApiProtocols } from "@/config/openclawProviderPresets"; import type { ProviderCategory, OpenClawModel } from "@/types"; @@ -101,6 +102,7 @@ export function OpenClawFormFields({ contextWindow: undefined, maxTokens: undefined, cost: undefined, + input: ["text"], }, ]); }; @@ -339,7 +341,66 @@ export function OpenClawFormFields({ - {/* Context Window, Max Tokens and Reasoning row */} + {/* Reasoning, Input Types row */} +
+
+ +
+ + handleModelChange(index, "reasoning", checked) + } + /> + + {model.reasoning + ? t("openclaw.reasoningOn", { + defaultValue: "启用", + }) + : t("openclaw.reasoningOff", { + defaultValue: "关闭", + })} + +
+
+
+ + {/* "text" is checked by default but can be unchecked — + some models genuinely don't support text input, and + OpenClaw works fine with an empty or image-only array. */} +
+ {(["text", "image"] as const).map((type) => ( + + ))} +
+
+
+
+ + {/* Context Window and Max Tokens row */}
-
- -
- - handleModelChange(index, "reasoning", checked) - } - /> - - {model.reasoning - ? t("openclaw.reasoningOn", { - defaultValue: "启用", - }) - : t("openclaw.reasoningOff", { - defaultValue: "关闭", - })} - -
-
+
{/* Cost row */} diff --git a/src/i18n/locales/en.json b/src/i18n/locales/en.json index 8eadfd45..afad6ea3 100644 --- a/src/i18n/locales/en.json +++ b/src/i18n/locales/en.json @@ -1336,6 +1336,7 @@ "reasoning": "Reasoning Mode", "reasoningOn": "Enabled", "reasoningOff": "Disabled", + "inputTypes": "Input Types", "inputCost": "Input Cost ($/M tokens)", "outputCost": "Output Cost ($/M tokens)", "advancedOptions": "Advanced Options", diff --git a/src/i18n/locales/ja.json b/src/i18n/locales/ja.json index 56c45079..915d4a70 100644 --- a/src/i18n/locales/ja.json +++ b/src/i18n/locales/ja.json @@ -1336,6 +1336,7 @@ "reasoning": "推論モード", "reasoningOn": "有効", "reasoningOff": "無効", + "inputTypes": "入力タイプ", "inputCost": "入力コスト ($/M トークン)", "outputCost": "出力コスト ($/M トークン)", "advancedOptions": "詳細オプション", diff --git a/src/i18n/locales/zh.json b/src/i18n/locales/zh.json index a850fcd6..45bacbb1 100644 --- a/src/i18n/locales/zh.json +++ b/src/i18n/locales/zh.json @@ -1336,6 +1336,7 @@ "reasoning": "推理模式", "reasoningOn": "启用", "reasoningOff": "关闭", + "inputTypes": "输入类型", "inputCost": "输入价格 ($/M tokens)", "outputCost": "输出价格 ($/M tokens)", "advancedOptions": "高级选项", From fab9874b2c7621611733f0d4984b7d331eeea154 Mon Sep 17 00:00:00 2001 From: bigsong <35025755+bigsongeth@users.noreply.github.com> Date: Tue, 10 Mar 2026 16:53:44 +0700 Subject: [PATCH 3/4] fix: align OpenClaw tool permission profiles with upstream schema (#1355) * fix: align OpenClaw tool permission profiles with upstream schema * fix: remove dead i18n keys and save-blocking validation - Remove unused `profiles.*` nested i18n keys (dead code, ToolsPanel uses flat `profileMinimal` etc.) - Remove `invalidProfile` i18n key no longer referenced - Remove handleSave validation that blocked saving allow/deny when legacy profile exists - Keep the profile destructuring cleanup from the original PR --------- Co-authored-by: Your Name Co-authored-by: Jason --- src/components/openclaw/ToolsPanel.tsx | 4 ++-- src/i18n/locales/en.json | 6 ------ src/i18n/locales/ja.json | 6 ------ src/i18n/locales/zh.json | 6 ------ 4 files changed, 2 insertions(+), 20 deletions(-) diff --git a/src/components/openclaw/ToolsPanel.tsx b/src/components/openclaw/ToolsPanel.tsx index 65b1f462..5d8ec09d 100644 --- a/src/components/openclaw/ToolsPanel.tsx +++ b/src/components/openclaw/ToolsPanel.tsx @@ -77,10 +77,10 @@ const ToolsPanel: React.FC = () => { const handleSave = async () => { try { - const { allow, deny, ...other } = config; + const { profile, allow, deny, ...other } = config; const newConfig: OpenClawToolsConfig = { ...other, - profile: config.profile, + profile, allow: allowList.map((item) => item.value).filter((s) => s.trim()), deny: denyList.map((item) => item.value).filter((s) => s.trim()), }; diff --git a/src/i18n/locales/en.json b/src/i18n/locales/en.json index afad6ea3..a57a3a51 100644 --- a/src/i18n/locales/en.json +++ b/src/i18n/locales/en.json @@ -1373,12 +1373,6 @@ "unsupportedProfileTitle": "Unsupported tools profile detected", "unsupportedProfileDescription": "The current tools.profile value '{{value}}' is not in the supported OpenClaw list. It will be preserved until you choose a new value.", "unsupportedProfileLabel": "unsupported", - "profiles": { - "default": "Default", - "strict": "Strict", - "permissive": "Permissive", - "custom": "Custom" - }, "allowList": "Allow List", "denyList": "Deny List", "patternPlaceholder": "Tool name or pattern", diff --git a/src/i18n/locales/ja.json b/src/i18n/locales/ja.json index 915d4a70..8567919d 100644 --- a/src/i18n/locales/ja.json +++ b/src/i18n/locales/ja.json @@ -1373,12 +1373,6 @@ "unsupportedProfileTitle": "未対応のツールプロファイルを検出しました", "unsupportedProfileDescription": "現在の tools.profile の値 '{{value}}' は OpenClaw の対応リストにありません。新しい値を選択するまでこの値を保持します。", "unsupportedProfileLabel": "未対応", - "profiles": { - "default": "デフォルト", - "strict": "厳格", - "permissive": "寛容", - "custom": "カスタム" - }, "allowList": "許可リスト", "denyList": "拒否リスト", "patternPlaceholder": "ツール名またはパターン", diff --git a/src/i18n/locales/zh.json b/src/i18n/locales/zh.json index 45bacbb1..449ccee3 100644 --- a/src/i18n/locales/zh.json +++ b/src/i18n/locales/zh.json @@ -1373,12 +1373,6 @@ "unsupportedProfileTitle": "检测到不受支持的工具配置", "unsupportedProfileDescription": "当前 tools.profile 的值“{{value}}”不在 OpenClaw 支持列表内。在你手动选择新值之前,它会被保留。", "unsupportedProfileLabel": "不受支持", - "profiles": { - "default": "默认", - "strict": "严格", - "permissive": "宽松", - "custom": "自定义" - }, "allowList": "允许列表", "denyList": "拒绝列表", "patternPlaceholder": "工具名称或模式", From 75b4ef229967408599909dd42ce62a1dbded3a71 Mon Sep 17 00:00:00 2001 From: Zhou Mengze Date: Tue, 10 Mar 2026 21:02:13 +0800 Subject: [PATCH 4/4] fix: interpolate proxy startup toast address and port (#1399) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: 周梦泽 --- src/hooks/useProxyStatus.ts | 2 + tests/hooks/useProxyStatus.test.tsx | 118 ++++++++++++++++++++++++++++ 2 files changed, 120 insertions(+) create mode 100644 tests/hooks/useProxyStatus.test.tsx diff --git a/src/hooks/useProxyStatus.ts b/src/hooks/useProxyStatus.ts index e2cf3c36..aa6d4a08 100644 --- a/src/hooks/useProxyStatus.ts +++ b/src/hooks/useProxyStatus.ts @@ -43,6 +43,8 @@ export function useProxyStatus() { onSuccess: (info) => { toast.success( t("proxy.server.started", { + address: info.address, + port: info.port, defaultValue: `代理服务已启动 - ${info.address}:${info.port}`, }), { closeButton: true }, diff --git a/tests/hooks/useProxyStatus.test.tsx b/tests/hooks/useProxyStatus.test.tsx new file mode 100644 index 00000000..ce18e4a0 --- /dev/null +++ b/tests/hooks/useProxyStatus.test.tsx @@ -0,0 +1,118 @@ +import type { ReactNode } from "react"; +import { renderHook, act, waitFor } from "@testing-library/react"; +import { QueryClientProvider } from "@tanstack/react-query"; +import { beforeEach, describe, expect, it, vi } from "vitest"; +import { useProxyStatus } from "@/hooks/useProxyStatus"; +import { createTestQueryClient } from "../utils/testQueryClient"; + +const toastSuccessMock = vi.fn(); +const toastErrorMock = vi.fn(); +const invokeMock = vi.fn(); + +vi.mock("sonner", () => ({ + toast: { + success: (...args: unknown[]) => toastSuccessMock(...args), + error: (...args: unknown[]) => toastErrorMock(...args), + }, +})); + +vi.mock("@tauri-apps/api/core", () => ({ + invoke: (...args: unknown[]) => invokeMock(...args), +})); + +vi.mock("react-i18next", () => ({ + useTranslation: () => ({ + t: (key: string, options?: Record) => { + if (key === "proxy.server.started") { + return `代理服务已启动 - ${options?.address}:${options?.port}`; + } + + if (typeof options?.defaultValue === "string") { + return options.defaultValue; + } + + return key; + }, + }), +})); + +interface WrapperProps { + children: ReactNode; +} + +function createWrapper() { + const queryClient = createTestQueryClient(); + + const wrapper = ({ children }: WrapperProps) => ( + {children} + ); + + return { wrapper, queryClient }; +} + +describe("useProxyStatus", () => { + beforeEach(() => { + invokeMock.mockReset(); + toastSuccessMock.mockReset(); + toastErrorMock.mockReset(); + + invokeMock.mockImplementation((command: string) => { + if (command === "get_proxy_status") { + return Promise.resolve({ + running: false, + address: "127.0.0.1", + port: 15721, + active_connections: 0, + total_requests: 0, + success_requests: 0, + failed_requests: 0, + success_rate: 0, + uptime_seconds: 0, + current_provider: null, + current_provider_id: null, + last_request_at: null, + last_error: null, + failover_count: 0, + }); + } + + if (command === "get_proxy_takeover_status") { + return Promise.resolve({ + claude: false, + codex: false, + gemini: false, + opencode: false, + openclaw: false, + }); + } + + if (command === "start_proxy_server") { + return Promise.resolve({ + address: "127.0.0.1", + port: 15721, + started_at: "2026-03-10T00:00:00Z", + }); + } + + return Promise.resolve(null); + }); + }); + + it("shows interpolated address and port after proxy server starts", async () => { + const { wrapper } = createWrapper(); + const { result } = renderHook(() => useProxyStatus(), { wrapper }); + + await waitFor(() => { + expect(result.current.isLoading).toBe(false); + }); + + await act(async () => { + await result.current.startProxyServer(); + }); + + expect(toastSuccessMock).toHaveBeenCalledWith( + "代理服务已启动 - 127.0.0.1:15721", + { closeButton: true }, + ); + }); +}); \ No newline at end of file