mirror of
https://github.com/farion1231/cc-switch.git
synced 2026-07-30 02:14:43 +08:00
feat: add Tool Search domain restriction bypass with active-installation patching
Resolve the active `claude` command from PATH and apply an equal-length byte patch to remove the domain whitelist check. Backups are stored in ~/.cc-switch/toolsearch-backups/ (SHA-256 of path) so they survive Claude Code version upgrades. The patch auto-reapplies on app startup when the setting is enabled. Frontend checks PatchResult.success and rolls back the setting on failure.
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import { useTranslation } from "react-i18next";
|
||||
import type { SettingsFormState } from "@/hooks/useSettings";
|
||||
import { AppWindow, MonitorUp, Power, EyeOff } from "lucide-react";
|
||||
import { AppWindow, MonitorUp, Power, EyeOff, Search } from "lucide-react";
|
||||
import { ToggleRow } from "@/components/ui/toggle-row";
|
||||
import { AnimatePresence, motion } from "framer-motion";
|
||||
|
||||
@@ -66,6 +66,14 @@ export function WindowSettings({ settings, onChange }: WindowSettingsProps) {
|
||||
onCheckedChange={(value) => onChange({ skipClaudeOnboarding: value })}
|
||||
/>
|
||||
|
||||
<ToggleRow
|
||||
icon={<Search className="h-4 w-4 text-amber-500" />}
|
||||
title={t("settings.toolSearchBypass")}
|
||||
description={t("settings.toolSearchBypassDescription")}
|
||||
checked={!!settings.toolSearchBypass}
|
||||
onCheckedChange={(value) => onChange({ toolSearchBypass: value })}
|
||||
/>
|
||||
|
||||
<ToggleRow
|
||||
icon={<AppWindow className="h-4 w-4 text-blue-500" />}
|
||||
title={t("settings.minimizeToTray")}
|
||||
|
||||
@@ -197,6 +197,44 @@ export function useSettings(): UseSettingsResult {
|
||||
}
|
||||
}
|
||||
|
||||
// Tool Search bypass: apply/restore patch when toggled
|
||||
const nextToolSearchBypass = updates.toolSearchBypass;
|
||||
if (
|
||||
nextToolSearchBypass !== undefined &&
|
||||
nextToolSearchBypass !== (data?.toolSearchBypass ?? false)
|
||||
) {
|
||||
try {
|
||||
const results = nextToolSearchBypass
|
||||
? await settingsApi.applyToolSearchPatch()
|
||||
: await settingsApi.restoreToolSearchPatch();
|
||||
const failed = results.find((r) => !r.success);
|
||||
if (failed) {
|
||||
throw new Error(failed.error ?? "Tool Search patch failed");
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn(
|
||||
"[useSettings] Failed to sync Tool Search bypass",
|
||||
error,
|
||||
);
|
||||
// Rollback: revert the setting we already saved
|
||||
const rolledBack = {
|
||||
...payload,
|
||||
toolSearchBypass: !nextToolSearchBypass,
|
||||
};
|
||||
await saveMutation.mutateAsync(rolledBack);
|
||||
updateSettings({ toolSearchBypass: !nextToolSearchBypass });
|
||||
toast.error(
|
||||
nextToolSearchBypass
|
||||
? t("notifications.toolSearchPatchFailed", {
|
||||
defaultValue: "Tool Search 补丁操作失败",
|
||||
})
|
||||
: t("notifications.toolSearchRestoreFailed", {
|
||||
defaultValue: "Tool Search 恢复操作失败",
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// 持久化语言偏好
|
||||
try {
|
||||
if (typeof window !== "undefined" && updates.language) {
|
||||
@@ -228,7 +266,7 @@ export function useSettings(): UseSettingsResult {
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
[data, saveMutation, settings, t],
|
||||
[data, saveMutation, settings, t, updateSettings],
|
||||
);
|
||||
|
||||
// 完整保存设置(用于 Advanced 标签页的手动保存)
|
||||
|
||||
@@ -85,6 +85,7 @@ export function useSettingsForm(): UseSettingsFormResult {
|
||||
data.enableClaudePluginIntegration ?? false,
|
||||
silentStartup: data.silentStartup ?? false,
|
||||
skipClaudeOnboarding: data.skipClaudeOnboarding ?? false,
|
||||
toolSearchBypass: data.toolSearchBypass ?? false,
|
||||
claudeConfigDir: sanitizeDir(data.claudeConfigDir),
|
||||
codexConfigDir: sanitizeDir(data.codexConfigDir),
|
||||
geminiConfigDir: sanitizeDir(data.geminiConfigDir),
|
||||
@@ -107,6 +108,7 @@ export function useSettingsForm(): UseSettingsFormResult {
|
||||
minimizeToTrayOnClose: true,
|
||||
enableClaudePluginIntegration: false,
|
||||
skipClaudeOnboarding: false,
|
||||
toolSearchBypass: false,
|
||||
language: readPersistedLanguage(),
|
||||
} as SettingsFormState);
|
||||
|
||||
@@ -143,6 +145,7 @@ export function useSettingsForm(): UseSettingsFormResult {
|
||||
serverData.enableClaudePluginIntegration ?? false,
|
||||
silentStartup: serverData.silentStartup ?? false,
|
||||
skipClaudeOnboarding: serverData.skipClaudeOnboarding ?? false,
|
||||
toolSearchBypass: serverData.toolSearchBypass ?? false,
|
||||
claudeConfigDir: sanitizeDir(serverData.claudeConfigDir),
|
||||
codexConfigDir: sanitizeDir(serverData.codexConfigDir),
|
||||
geminiConfigDir: sanitizeDir(serverData.geminiConfigDir),
|
||||
|
||||
@@ -166,6 +166,8 @@
|
||||
"syncClaudePluginFailed": "Sync Claude plugin failed",
|
||||
"skipClaudeOnboardingFailed": "Failed to skip Claude Code first-run confirmation",
|
||||
"clearClaudeOnboardingSkipFailed": "Failed to restore Claude Code first-run confirmation",
|
||||
"toolSearchPatchFailed": "Failed to apply Tool Search patch",
|
||||
"toolSearchRestoreFailed": "Failed to restore Tool Search patch",
|
||||
"updateSuccess": "Provider updated successfully",
|
||||
"updateFailed": "Failed to update provider: {{error}}",
|
||||
"deleteSuccess": "Provider deleted",
|
||||
@@ -463,6 +465,8 @@
|
||||
"enableClaudePluginIntegrationDescription": "When enabled, the VS Code Claude Code extension provider will switch with this app",
|
||||
"skipClaudeOnboarding": "Skip Claude Code first-run confirmation",
|
||||
"skipClaudeOnboardingDescription": "When enabled, Claude Code will skip the first-run confirmation",
|
||||
"toolSearchBypass": "Bypass Tool Search domain restriction",
|
||||
"toolSearchBypassDescription": "Remove the Tool Search domain whitelist in the active Claude Code installation. Auto-reapplied after that installation updates",
|
||||
"appVisibility": {
|
||||
"title": "Homepage Display",
|
||||
"description": "Choose which apps to show on the homepage",
|
||||
|
||||
@@ -166,6 +166,8 @@
|
||||
"syncClaudePluginFailed": "Claude プラグインとの同期に失敗しました",
|
||||
"skipClaudeOnboardingFailed": "Claude Code の初回確認スキップに失敗しました",
|
||||
"clearClaudeOnboardingSkipFailed": "Claude Code の初回確認の復元に失敗しました",
|
||||
"toolSearchPatchFailed": "Tool Search パッチの適用に失敗しました",
|
||||
"toolSearchRestoreFailed": "Tool Search パッチの復元に失敗しました",
|
||||
"updateSuccess": "プロバイダーを更新しました",
|
||||
"updateFailed": "プロバイダーの更新に失敗しました: {{error}}",
|
||||
"deleteSuccess": "プロバイダーを削除しました",
|
||||
@@ -463,6 +465,8 @@
|
||||
"enableClaudePluginIntegrationDescription": "オンにすると VS Code の Claude Code 拡張のプロバイダーも同期します",
|
||||
"skipClaudeOnboarding": "Claude Code の初回確認をスキップ",
|
||||
"skipClaudeOnboardingDescription": "オンにすると Claude Code の初回インストール確認をスキップします",
|
||||
"toolSearchBypass": "Tool Search ドメイン制限を解除",
|
||||
"toolSearchBypassDescription": "現在アクティブな Claude Code インストールの Tool Search ドメインホワイトリスト制限を解除します。そのインストールの更新後は自動で再適用されます",
|
||||
"appVisibility": {
|
||||
"title": "ホームページ表示",
|
||||
"description": "ホームページに表示するアプリを選択",
|
||||
|
||||
@@ -166,6 +166,8 @@
|
||||
"syncClaudePluginFailed": "同步 Claude 插件失败",
|
||||
"skipClaudeOnboardingFailed": "跳过 Claude Code 初次安装确认失败",
|
||||
"clearClaudeOnboardingSkipFailed": "恢复 Claude Code 初次安装确认失败",
|
||||
"toolSearchPatchFailed": "Tool Search 补丁操作失败",
|
||||
"toolSearchRestoreFailed": "Tool Search 恢复操作失败",
|
||||
"updateSuccess": "供应商更新成功",
|
||||
"updateFailed": "更新供应商失败:{{error}}",
|
||||
"deleteSuccess": "供应商已删除",
|
||||
@@ -463,6 +465,8 @@
|
||||
"enableClaudePluginIntegrationDescription": "开启后 Vscode Claude Code 插件的供应商将随本软件切换",
|
||||
"skipClaudeOnboarding": "跳过 Claude Code 初次安装确认",
|
||||
"skipClaudeOnboardingDescription": "开启后跳过 Claude Code 初次安装确认",
|
||||
"toolSearchBypass": "解除 Tool Search 域名限制",
|
||||
"toolSearchBypassDescription": "解除当前活跃 Claude Code 安装的 Tool Search 域名白名单限制。该安装更新后会自动重新应用",
|
||||
"appVisibility": {
|
||||
"title": "主页面显示",
|
||||
"description": "选择在主页面显示的应用",
|
||||
|
||||
@@ -86,6 +86,18 @@ export const settingsApi = {
|
||||
return await invoke("clear_claude_onboarding_skip");
|
||||
},
|
||||
|
||||
async applyToolSearchPatch(): Promise<
|
||||
Array<{ path: string; success: boolean; error?: string }>
|
||||
> {
|
||||
return await invoke("apply_toolsearch_patch");
|
||||
},
|
||||
|
||||
async restoreToolSearchPatch(): Promise<
|
||||
Array<{ path: string; success: boolean; error?: string }>
|
||||
> {
|
||||
return await invoke("restore_toolsearch_patch");
|
||||
},
|
||||
|
||||
async saveFileDialog(defaultName: string): Promise<string | null> {
|
||||
return await invoke("save_file_dialog", { defaultName });
|
||||
},
|
||||
|
||||
@@ -226,6 +226,8 @@ export interface Settings {
|
||||
enableClaudePluginIntegration?: boolean;
|
||||
// 跳过 Claude Code 初次安装确认(写入 ~/.claude.json 的 hasCompletedOnboarding)
|
||||
skipClaudeOnboarding?: boolean;
|
||||
// 解除 Tool Search 域名限制
|
||||
toolSearchBypass?: boolean;
|
||||
// 是否开机自启
|
||||
launchOnStartup?: boolean;
|
||||
// 静默启动(程序启动时不显示主窗口)
|
||||
|
||||
Reference in New Issue
Block a user