mirror of
https://github.com/farion1231/cc-switch.git
synced 2026-10-25 17:22:27 +08:00
feat(claude): add auto mode server-check toggle and default it off for gateways
Add a "Disable auto mode server check" quick toggle to the Claude provider editor that writes CLAUDE_CODE_AUTO_MODE_SERVER=0, with a hint explaining that third-party gateways cannot serve the check and that official endpoints should leave it unchecked. New Claude providers now get the key by default unless the preset is official or a cloud provider, or the preset already sets it. The default form (no live base yet) also goes through the overlay so the custom provider starts with the same value.
This commit is contained in:
1 parent
3144c820f1
commit
146f3196fa
10 files changed
+156
-7
No files matched your search
@@ -3,6 +3,7 @@ import { useEffect, useState, useCallback, useMemo } from "react";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import JsonEditor from "@/components/JsonEditor";
|
||||
import type { ProviderEditorInactiveField } from "@/lib/api/providers";
|
||||
import { CLAUDE_AUTO_MODE_SERVER_ENV } from "@/utils/claudeEditorOverlay";
|
||||
import { InactiveFieldsPanel } from "./InactiveFieldsPanel";
|
||||
|
||||
interface CommonConfigEditorProps {
|
||||
@@ -94,6 +95,9 @@ export function CommonConfigEditor({
|
||||
disableArtifact:
|
||||
config?.env?.CLAUDE_CODE_DISABLE_ARTIFACT === "1" ||
|
||||
config?.env?.CLAUDE_CODE_DISABLE_ARTIFACT === 1,
|
||||
disableAutoModeServer:
|
||||
config?.env?.[CLAUDE_AUTO_MODE_SERVER_ENV] === "0" ||
|
||||
config?.env?.[CLAUDE_AUTO_MODE_SERVER_ENV] === 0,
|
||||
};
|
||||
} catch {
|
||||
return {
|
||||
@@ -103,6 +107,7 @@ export function CommonConfigEditor({
|
||||
effortMax: false,
|
||||
disableAutoUpgrade: false,
|
||||
disableArtifact: false,
|
||||
disableAutoModeServer: false,
|
||||
};
|
||||
}
|
||||
}, [localValue]);
|
||||
@@ -168,6 +173,15 @@ export function CommonConfigEditor({
|
||||
if (Object.keys(config.env).length === 0) delete config.env;
|
||||
}
|
||||
break;
|
||||
case "disableAutoModeServer":
|
||||
if (!config.env) config.env = {};
|
||||
if (checked) {
|
||||
config.env[CLAUDE_AUTO_MODE_SERVER_ENV] = "0";
|
||||
} else {
|
||||
delete config.env[CLAUDE_AUTO_MODE_SERVER_ENV];
|
||||
if (Object.keys(config.env).length === 0) delete config.env;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
handleLocalChange(JSON.stringify(config, null, 2));
|
||||
@@ -270,6 +284,20 @@ export function CommonConfigEditor({
|
||||
/>
|
||||
<span>{t("claudeConfig.disableArtifact")}</span>
|
||||
</label>
|
||||
<label
|
||||
className="inline-flex items-center gap-2 text-sm text-fg-2 cursor-pointer"
|
||||
title={t("claudeConfig.disableAutoModeServerHint")}
|
||||
>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={toggleStates.disableAutoModeServer}
|
||||
onChange={(e) =>
|
||||
handleToggle("disableAutoModeServer", e.target.checked)
|
||||
}
|
||||
className="ui-checkbox"
|
||||
/>
|
||||
<span>{t("claudeConfig.disableAutoModeServer")}</span>
|
||||
</label>
|
||||
</div>
|
||||
<JsonEditor
|
||||
value={localValue}
|
||||
|
||||
@@ -14,7 +14,10 @@ import {
|
||||
} from "@/lib/requestOverrides";
|
||||
import { providersApi, type AppId, type ManagedAuthProvider } from "@/lib/api";
|
||||
import type { ProviderEditorInactiveField } from "@/lib/api/providers";
|
||||
import { overlayClaudeProviderFields } from "@/utils/claudeEditorOverlay";
|
||||
import {
|
||||
overlayClaudeProviderFields,
|
||||
withClaudeGatewayDefaults,
|
||||
} from "@/utils/claudeEditorOverlay";
|
||||
import { useDarkMode } from "@/hooks/useDarkMode";
|
||||
import type {
|
||||
ProviderCategory,
|
||||
@@ -452,11 +455,14 @@ function ProviderFormFull({
|
||||
notes: initialData?.notes ?? "",
|
||||
settingsConfig: initialData?.settingsConfig
|
||||
? JSON.stringify(initialData.settingsConfig, null, 2)
|
||||
: appId === "claude" && claudeLiveBase
|
||||
: appId === "claude"
|
||||
? JSON.stringify(
|
||||
overlayClaudeProviderFields(
|
||||
claudeLiveBase,
|
||||
JSON.parse(CLAUDE_DEFAULT_CONFIG) as Record<string, unknown>,
|
||||
claudeLiveBase ?? {},
|
||||
withClaudeGatewayDefaults(
|
||||
JSON.parse(CLAUDE_DEFAULT_CONFIG) as Record<string, unknown>,
|
||||
"custom",
|
||||
),
|
||||
),
|
||||
null,
|
||||
2,
|
||||
@@ -2211,10 +2217,13 @@ function ProviderFormFull({
|
||||
);
|
||||
// 预设只带关键字段和独有字段,套在当前 live 上显示(和切换的结果一致)。
|
||||
const config =
|
||||
appId === "claude" && claudeLiveBase
|
||||
appId === "claude"
|
||||
? overlayClaudeProviderFields(
|
||||
claudeLiveBase,
|
||||
templated as Record<string, unknown>,
|
||||
claudeLiveBase ?? {},
|
||||
withClaudeGatewayDefaults(
|
||||
templated as Record<string, unknown>,
|
||||
preset.category,
|
||||
),
|
||||
)
|
||||
: templated;
|
||||
|
||||
|
||||
@@ -76,6 +76,8 @@
|
||||
"effortMax": "Max Effort Thinking",
|
||||
"disableAutoUpgrade": "Disable Auto-Upgrade",
|
||||
"disableArtifact": "Disable Artifact Tool",
|
||||
"disableAutoModeServer": "Disable Auto Mode Server Checks",
|
||||
"disableAutoModeServerHint": "Third-party gateways can't run auto mode's server-side checks. When on, Claude Code uses its own checks and stops showing the \"this session isn't eligible\" notice in every session (CLAUDE_CODE_AUTO_MODE_SERVER=0). Leave off for the official endpoint.",
|
||||
"keyFieldsHint": "Endpoint, key, models, context window and compatibility switches follow the provider; everything else is a global Claude Code setting and applies to every provider once saved.",
|
||||
"inactiveFieldsHint": "This provider still stores {{count}} field(s) that do not take effect when switching. Click one to add it to the global settings above; it is written to the config file on save, and the value stored in the provider is kept.",
|
||||
"addToGlobalSettings": "Add to global settings"
|
||||
|
||||
@@ -76,6 +76,8 @@
|
||||
"effortMax": "最大強度思考",
|
||||
"disableAutoUpgrade": "自動アップグレードを無効化",
|
||||
"disableArtifact": "Artifact ツールを無効化",
|
||||
"disableAutoModeServer": "auto mode のサーバー側チェックを無効化",
|
||||
"disableAutoModeServerHint": "サードパーティのゲートウェイは auto mode のサーバー側チェックに対応していません。オンにすると Claude Code は自前のチェックを使い、セッションごとの「このセッションは対象外です」という通知が出なくなります(CLAUDE_CODE_AUTO_MODE_SERVER=0)。公式エンドポイントではオフのままにしてください。",
|
||||
"keyFieldsHint": "エンドポイント、キー、モデル、コンテキストウィンドウ、互換スイッチはプロバイダーの切り替えに従います。それ以外のフィールドは Claude Code のグローバル設定で、保存するとすべてのプロバイダーに適用されます。",
|
||||
"inactiveFieldsHint": "このプロバイダーには、切り替えても反映されないフィールドが {{count}} 件保存されています。クリックすると上のグローバル設定に追加され、保存時に設定ファイルへ書き込まれます。プロバイダーに保存された元の値は削除されません。",
|
||||
"addToGlobalSettings": "グローバル設定に追加"
|
||||
|
||||
@@ -76,6 +76,8 @@
|
||||
"effortMax": "最大強度思考",
|
||||
"disableAutoUpgrade": "停用自動升級",
|
||||
"disableArtifact": "停用 Artifact 工具",
|
||||
"disableAutoModeServer": "停用 auto mode 伺服器端檢查",
|
||||
"disableAutoModeServerHint": "第三方閘道不支援 auto mode 的伺服器端檢查。勾選後 Claude Code 改用自己的檢查,不再每個工作階段跳出「此工作階段不符合資格」的提示(CLAUDE_CODE_AUTO_MODE_SERVER=0)。官方端點請勿勾選。",
|
||||
"keyFieldsHint": "位址、Key、模型、上下文視窗和相容開關隨供應商切換;其餘欄位是 Claude Code 全域設定,儲存後對所有供應商生效。",
|
||||
"inactiveFieldsHint": "這個供應商還保存著 {{count}} 個不隨切換生效的欄位。點擊可加入上方的全域設定,儲存後寫入設定檔;供應商裡保存的原值不會刪除。",
|
||||
"addToGlobalSettings": "加入全域設定"
|
||||
|
||||
@@ -76,6 +76,8 @@
|
||||
"effortMax": "最大强度思考",
|
||||
"disableAutoUpgrade": "禁用自动升级",
|
||||
"disableArtifact": "禁用 Artifact 工具",
|
||||
"disableAutoModeServer": "关闭 auto mode 服务端检查",
|
||||
"disableAutoModeServerHint": "第三方网关不支持 auto mode 的服务端检查。勾选后 Claude Code 改用自己的检查,不再每个会话弹出“本会话不符合条件”的提示(CLAUDE_CODE_AUTO_MODE_SERVER=0)。官方端点不要勾选。",
|
||||
"keyFieldsHint": "地址、Key、模型、上下文窗口和兼容开关随供应商切换;其余字段是 Claude Code 全局设置,保存后对所有供应商生效。",
|
||||
"inactiveFieldsHint": "这个供应商还保存着 {{count}} 个不随切换生效的字段。点击可加入上方的全局设置,保存后写入配置文件;供应商里保存的原值不会删除。",
|
||||
"addToGlobalSettings": "加入全局设置"
|
||||
|
||||
@@ -1,6 +1,30 @@
|
||||
const isRecord = (value: unknown): value is Record<string, unknown> =>
|
||||
typeof value === "object" && value !== null && !Array.isArray(value);
|
||||
|
||||
/**
|
||||
* auto mode 的服务端检查只有官方端点支持,经网关时 Claude Code 每个会话都会弹一次需要
|
||||
* 确认的提示;设成 "0" 改用 Claude Code 自己的检查,提示不再出现。
|
||||
*/
|
||||
export const CLAUDE_AUTO_MODE_SERVER_ENV = "CLAUDE_CODE_AUTO_MODE_SERVER";
|
||||
|
||||
/**
|
||||
* 新增 Claude 供应商时补上网关默认值:除官方和云服务商(Bedrock、Vertex 直连,服务端
|
||||
* 检查随平台上线)外,默认关掉 auto mode 的服务端检查。预设自己写了的以预设为准。
|
||||
*/
|
||||
export function withClaudeGatewayDefaults(
|
||||
fields: Record<string, unknown>,
|
||||
category: string | undefined,
|
||||
): Record<string, unknown> {
|
||||
if (category === "official" || category === "cloud_provider") {
|
||||
return fields;
|
||||
}
|
||||
const env = isRecord(fields.env) ? fields.env : {};
|
||||
if (CLAUDE_AUTO_MODE_SERVER_ENV in env) {
|
||||
return fields;
|
||||
}
|
||||
return { ...fields, env: { ...env, [CLAUDE_AUTO_MODE_SERVER_ENV]: "0" } };
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增 Claude 供应商时,把预设的字段(只有关键字段和独有字段)套在当前 live 上显示,
|
||||
* 和切过去之后的结果一致:预设的顶层键、`env` 键覆盖 live 的同名键,其余保持 live 的
|
||||
|
||||
@@ -152,3 +152,43 @@ describe("CommonConfigEditor inactive row fields", () => {
|
||||
).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe("CommonConfigEditor auto mode server toggle", () => {
|
||||
const autoModeServerCheckbox = () =>
|
||||
screen.getByRole("checkbox", {
|
||||
name: "claudeConfig.disableAutoModeServer",
|
||||
});
|
||||
|
||||
it("reads CLAUDE_CODE_AUTO_MODE_SERVER=0 as checked", () => {
|
||||
renderEditor(
|
||||
JSON.stringify({ env: { CLAUDE_CODE_AUTO_MODE_SERVER: "0" } }),
|
||||
);
|
||||
|
||||
expect(autoModeServerCheckbox()).toBeChecked();
|
||||
});
|
||||
|
||||
it("writes and removes the env key", () => {
|
||||
const onChange = renderEditor(
|
||||
JSON.stringify({ env: { ANTHROPIC_BASE_URL: "https://gw.example" } }),
|
||||
);
|
||||
|
||||
fireEvent.click(autoModeServerCheckbox());
|
||||
|
||||
expect(JSON.parse(onChange.mock.calls[0][0])).toEqual({
|
||||
env: {
|
||||
ANTHROPIC_BASE_URL: "https://gw.example",
|
||||
CLAUDE_CODE_AUTO_MODE_SERVER: "0",
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it("drops an emptied env when unchecked", () => {
|
||||
const onChange = renderEditor(
|
||||
JSON.stringify({ env: { CLAUDE_CODE_AUTO_MODE_SERVER: "0" } }),
|
||||
);
|
||||
|
||||
fireEvent.click(autoModeServerCheckbox());
|
||||
|
||||
expect(JSON.parse(onChange.mock.calls[0][0])).toEqual({});
|
||||
});
|
||||
});
|
||||
@@ -74,6 +74,7 @@ exports[`ProviderForm 预设新增行金标 > claude > E-FlowCode 1`] = `
|
||||
"env": {
|
||||
"ANTHROPIC_AUTH_TOKEN": "sk-golden-test",
|
||||
"ANTHROPIC_BASE_URL": "https://e-flowcode.cc",
|
||||
"CLAUDE_CODE_AUTO_MODE_SERVER": "0",
|
||||
"ENABLE_TOOL_SEARCH": "true",
|
||||
},
|
||||
},
|
||||
@@ -107,6 +108,7 @@ exports[`ProviderForm 预设新增行金标 > claude > Kimi For Coding 1`] = `
|
||||
"ANTHROPIC_DEFAULT_SONNET_MODEL": "kimi-for-coding",
|
||||
"ANTHROPIC_MODEL": "kimi-for-coding",
|
||||
"CLAUDE_CODE_AUTO_COMPACT_WINDOW": "262144",
|
||||
"CLAUDE_CODE_AUTO_MODE_SERVER": "0",
|
||||
"CLAUDE_CODE_MAX_CONTEXT_TOKENS": "262144",
|
||||
},
|
||||
},
|
||||
@@ -139,6 +141,7 @@ exports[`ProviderForm 预设新增行金标 > claude > Nvidia 1`] = `
|
||||
"ANTHROPIC_DEFAULT_OPUS_MODEL": "moonshotai/kimi-k3",
|
||||
"ANTHROPIC_DEFAULT_SONNET_MODEL": "moonshotai/kimi-k3",
|
||||
"ANTHROPIC_MODEL": "moonshotai/kimi-k3",
|
||||
"CLAUDE_CODE_AUTO_MODE_SERVER": "0",
|
||||
},
|
||||
},
|
||||
"websiteUrl": "https://build.nvidia.com",
|
||||
@@ -166,6 +169,7 @@ exports[`ProviderForm 预设新增行金标 > claude > RelaxyCode 1`] = `
|
||||
"env": {
|
||||
"ANTHROPIC_AUTH_TOKEN": "sk-golden-test",
|
||||
"ANTHROPIC_BASE_URL": "https://www.relaxycode.com",
|
||||
"CLAUDE_CODE_AUTO_MODE_SERVER": "0",
|
||||
},
|
||||
},
|
||||
"websiteUrl": "https://www.relaxycode.com",
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { withClaudeGatewayDefaults } from "@/utils/claudeEditorOverlay";
|
||||
|
||||
describe("withClaudeGatewayDefaults", () => {
|
||||
const gateway = { env: { ANTHROPIC_BASE_URL: "https://gw.example" } };
|
||||
|
||||
it.each(["third_party", "aggregator", "cn_official", "custom", undefined])(
|
||||
"turns auto mode server checks off for %s providers",
|
||||
(category) => {
|
||||
expect(withClaudeGatewayDefaults(gateway, category)).toEqual({
|
||||
env: {
|
||||
ANTHROPIC_BASE_URL: "https://gw.example",
|
||||
CLAUDE_CODE_AUTO_MODE_SERVER: "0",
|
||||
},
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
it.each(["official", "cloud_provider"])(
|
||||
"leaves %s providers on the default",
|
||||
(category) => {
|
||||
expect(withClaudeGatewayDefaults(gateway, category)).toBe(gateway);
|
||||
},
|
||||
);
|
||||
|
||||
it("keeps a value the preset sets itself", () => {
|
||||
const preset = { env: { CLAUDE_CODE_AUTO_MODE_SERVER: "1" } };
|
||||
expect(withClaudeGatewayDefaults(preset, "third_party")).toBe(preset);
|
||||
});
|
||||
|
||||
it("adds env when the preset has none", () => {
|
||||
expect(withClaudeGatewayDefaults({}, "custom")).toEqual({
|
||||
env: { CLAUDE_CODE_AUTO_MODE_SERVER: "0" },
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user