From 2e77646d607676fee1297948878cd4ba1939c58f Mon Sep 17 00:00:00 2001 From: kaitranntt Date: Thu, 18 Dec 2025 00:20:25 -0500 Subject: [PATCH 1/4] fix(ui): add error state handling to API Profiles page Adds proper error handling when useProfiles() hook fails to fetch data. Previously, API errors would cause a blank screen as the component had no error state handling. Now shows a user-friendly error message with a retry button. Fixes #125 --- ui/src/pages/api.tsx | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/ui/src/pages/api.tsx b/ui/src/pages/api.tsx index cde4a4d6..62fe0840 100644 --- a/ui/src/pages/api.tsx +++ b/ui/src/pages/api.tsx @@ -19,6 +19,7 @@ import { Server, ExternalLink, FileJson, + RefreshCw, } from 'lucide-react'; import { ProfileEditor } from '@/components/profile-editor'; import { ProfileCreateDialog } from '@/components/profile-create-dialog'; @@ -29,7 +30,7 @@ import { cn } from '@/lib/utils'; import { CopyButton } from '@/components/ui/copy-button'; export function ApiPage() { - const { data, isLoading } = useProfiles(); + const { data, isLoading, isError, refetch } = useProfiles(); const deleteMutation = useDeleteProfile(); const [selectedProfile, setSelectedProfile] = useState(null); const [searchQuery, setSearchQuery] = useState(''); @@ -111,6 +112,22 @@ export function ApiPage() { {isLoading ? (
Loading profiles...
+ ) : isError ? ( +
+
+ +
+

Failed to load profiles

+

+ Unable to fetch API profiles. Please try again. +

+
+ +
+
) : filteredProfiles.length === 0 ? (
{profiles.length === 0 ? ( From 7b40929a78c73e544b6d623c57a98d68709de2a4 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 18 Dec 2025 05:27:35 +0000 Subject: [PATCH 2/4] chore(release): 6.2.0-dev.1 [skip ci] --- VERSION | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/VERSION b/VERSION index 6abaeb2f..fd0e9a22 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -6.2.0 +6.2.0-dev.1 diff --git a/package.json b/package.json index a6e2e0d0..828a00c0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@kaitranntt/ccs", - "version": "6.2.0", + "version": "6.2.0-dev.1", "description": "Claude Code Switch - Instant profile switching between Claude Sonnet 4.5 and GLM 4.6", "keywords": [ "cli", From 6bd1f420d994e799125f338940689e969e524991 Mon Sep 17 00:00:00 2001 From: kaitranntt Date: Thu, 18 Dec 2025 00:27:40 -0500 Subject: [PATCH 3/4] fix(websearch): pass through to native WebSearch for account profiles WebSearch hook was blocking native Claude accounts (ccs ck, default) with "No Providers Enabled" message instead of passing through to server-side WebSearch. Changes: - Add shouldSkipHook() detection for account/default profile types - Fix blocking bug: exit(0) when no providers enabled instead of blocking - Pass CCS_PROFILE_TYPE and CCS_WEBSEARCH_SKIP env vars for profile-aware behavior - Add profile type signals to settings, cliproxy, account, and default execution paths --- lib/hooks/websearch-transformer.cjs | 31 +++++++++++++++++++++-------- src/ccs.ts | 28 ++++++++++++++++++++++---- src/cliproxy/cliproxy-executor.ts | 7 ++++++- 3 files changed, 53 insertions(+), 13 deletions(-) diff --git a/lib/hooks/websearch-transformer.cjs b/lib/hooks/websearch-transformer.cjs index 4384a63c..84d9f8d7 100644 --- a/lib/hooks/websearch-transformer.cjs +++ b/lib/hooks/websearch-transformer.cjs @@ -116,6 +116,24 @@ const DEFAULT_TIMEOUT_SEC = 55; // HOOK LOGIC - Generally no need to edit below // ============================================================================ +/** + * Determine if hook should skip and pass through to native WebSearch. + * Returns true for native Claude accounts where WebSearch works server-side. + */ +function shouldSkipHook() { + // Explicit skip signal (set by CCS for account profiles) + if (process.env.CCS_WEBSEARCH_SKIP === '1') return true; + + // Account/default profiles - use native WebSearch + const profileType = process.env.CCS_PROFILE_TYPE; + if (profileType === 'account' || profileType === 'default') return true; + + // Explicit disable + if (process.env.CCS_WEBSEARCH_ENABLED === '0') return true; + + return false; +} + // Read input from stdin let input = ''; process.stdin.setEncoding('utf8'); @@ -164,13 +182,8 @@ function isProviderEnabled(provider) { */ async function processHook() { try { - // Skip if disabled (for official Claude subscriptions) - if (process.env.CCS_WEBSEARCH_SKIP === '1') { - process.exit(0); - } - - // Check if enabled (default: enabled) - if (process.env.CCS_WEBSEARCH_ENABLED === '0') { + // Skip for native accounts (account, default profiles) or explicit disable + if (shouldSkipHook()) { process.exit(0); } @@ -238,7 +251,9 @@ async function processHook() { // All providers failed or none enabled if (enabledProviders.length === 0) { - outputNoProvidersEnabled(query); + // No providers enabled - pass through to native WebSearch + // This allows native Claude accounts to use server-side WebSearch + process.exit(0); } else { outputAllFailedMessage(query, errors); } diff --git a/src/ccs.ts b/src/ccs.ts index 3e1c22ef..437156a1 100644 --- a/src/ccs.ts +++ b/src/ccs.ts @@ -154,7 +154,12 @@ async function execClaudeWithProxy( const isWindows = process.platform === 'win32'; const needsShell = isWindows && /\.(cmd|bat|ps1)$/i.test(claudeCli); const webSearchEnv = getWebSearchHookEnv(); - const env = { ...process.env, ...envVars, ...webSearchEnv }; + const env = { + ...process.env, + ...envVars, + ...webSearchEnv, + CCS_PROFILE_TYPE: 'settings', // Signal to WebSearch hook this is a third-party provider + }; let claude: ChildProcess; if (needsShell) { @@ -401,7 +406,12 @@ async function main(): Promise { // EXISTING FLOW: Settings-based profile (glm, kimi) // Use --settings flag (backward compatible) const expandedSettingsPath = getSettingsPath(profileInfo.name); - execClaude(claudeCli, ['--settings', expandedSettingsPath, ...remainingArgs]); + const webSearchEnv = getWebSearchHookEnv(); + const envVars: NodeJS.ProcessEnv = { + ...webSearchEnv, + CCS_PROFILE_TYPE: 'settings', // Signal to WebSearch hook this is a third-party provider + }; + execClaude(claudeCli, ['--settings', expandedSettingsPath, ...remainingArgs], envVars); } } else if (profileInfo.type === 'account') { // NEW FLOW: Account-based profile (work, personal) @@ -420,11 +430,21 @@ async function main(): Promise { } // Execute Claude with instance isolation - const envVars: NodeJS.ProcessEnv = { CLAUDE_CONFIG_DIR: instancePath }; + // Skip WebSearch hook - account profiles use native server-side WebSearch + const envVars: NodeJS.ProcessEnv = { + CLAUDE_CONFIG_DIR: instancePath, + CCS_PROFILE_TYPE: 'account', + CCS_WEBSEARCH_SKIP: '1', + }; execClaude(claudeCli, remainingArgs, envVars); } else { // DEFAULT: No profile configured, use Claude's own defaults - execClaude(claudeCli, remainingArgs); + // Skip WebSearch hook - native Claude has server-side WebSearch + const envVars: NodeJS.ProcessEnv = { + CCS_PROFILE_TYPE: 'default', + CCS_WEBSEARCH_SKIP: '1', + }; + execClaude(claudeCli, remainingArgs, envVars); } } catch (error) { const err = error as ProfileError; diff --git a/src/cliproxy/cliproxy-executor.ts b/src/cliproxy/cliproxy-executor.ts index 3b69a2f6..f874a8dd 100644 --- a/src/cliproxy/cliproxy-executor.ts +++ b/src/cliproxy/cliproxy-executor.ts @@ -426,7 +426,12 @@ export async function execClaudeWithCLIProxy( // Uses custom settings path (for variants), user settings, or bundled defaults const envVars = getEffectiveEnvVars(provider, cfg.port, cfg.customSettingsPath); const webSearchEnv = getWebSearchHookEnv(); - const env = { ...process.env, ...envVars, ...webSearchEnv }; + const env = { + ...process.env, + ...envVars, + ...webSearchEnv, + CCS_PROFILE_TYPE: 'cliproxy', // Signal to WebSearch hook this is a third-party provider + }; log(`Claude env: ANTHROPIC_BASE_URL=${envVars.ANTHROPIC_BASE_URL}`); log(`Claude env: ANTHROPIC_MODEL=${envVars.ANTHROPIC_MODEL}`); From 641dc4b67ae1133254372d4b1d3bc9147f9aa5c1 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 18 Dec 2025 05:34:22 +0000 Subject: [PATCH 4/4] chore(release): 6.2.0-dev.2 [skip ci] --- VERSION | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/VERSION b/VERSION index fd0e9a22..f09fb39a 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -6.2.0-dev.1 +6.2.0-dev.2 diff --git a/package.json b/package.json index 828a00c0..02b0cdc2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@kaitranntt/ccs", - "version": "6.2.0-dev.1", + "version": "6.2.0-dev.2", "description": "Claude Code Switch - Instant profile switching between Claude Sonnet 4.5 and GLM 4.6", "keywords": [ "cli",