diff --git a/package.json b/package.json index a8bee929..473da93e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@kaitranntt/ccs", - "version": "7.28.0", + "version": "7.28.0-dev.2", "description": "Claude Code Switch - Instant profile switching between Claude Sonnet 4.5 and GLM 4.6", "keywords": [ "cli", diff --git a/src/ccs.ts b/src/ccs.ts index 8fce1c51..84a65178 100644 --- a/src/ccs.ts +++ b/src/ccs.ts @@ -8,7 +8,6 @@ import { ErrorManager } from './utils/error-manager'; import { execClaudeWithCLIProxy, CLIProxyProvider } from './cliproxy'; import { ensureMcpWebSearch, - installWebSearchHook, displayWebSearchStatus, getWebSearchHookEnv, ensureProfileHooks, @@ -549,7 +548,6 @@ async function main(): Promise { ensureProfileHooks(profileInfo.name); ensureMcpWebSearch(); - installWebSearchHook(); // Display WebSearch status (single line, equilibrium UX) displayWebSearchStatus(); diff --git a/src/cliproxy/config-generator.ts b/src/cliproxy/config-generator.ts index 5abc9d44..a4652417 100644 --- a/src/cliproxy/config-generator.ts +++ b/src/cliproxy/config-generator.ts @@ -286,8 +286,9 @@ export function getCliproxyWritablePath(): string { * v2: Full-featured config with dashboard, quota mgmt, simplified key * v3: Logging disabled by default (user opt-in via ~/.ccs/config.yaml) * v4: Added Kiro (AWS) and GitHub Copilot providers + * v5: Added disable-cooling: true for stability */ -export const CLIPROXY_CONFIG_VERSION = 4; +export const CLIPROXY_CONFIG_VERSION = 5; /** Provider display names (static metadata) */ const PROVIDER_DISPLAY_NAMES: Record = { @@ -471,6 +472,9 @@ remote-management: # Reliability & Quota Management # ============================================================================= +# Disable quota cooldown scheduling for stability +disable-cooling: true + # Auto-retry on transient errors (403, 408, 500, 502, 503, 504) request-retry: 0 max-retry-interval: 0 diff --git a/src/cliproxy/platform-detector.ts b/src/cliproxy/platform-detector.ts index e2a4dacb..28e4ce21 100644 --- a/src/cliproxy/platform-detector.ts +++ b/src/cliproxy/platform-detector.ts @@ -19,13 +19,13 @@ export const BACKEND_CONFIG = { repo: 'router-for-me/CLIProxyAPI', binaryPrefix: 'CLIProxyAPI', executable: 'cli-proxy-api', - fallbackVersion: '6.7.8', + fallbackVersion: '6.7.25', }, plus: { repo: 'router-for-me/CLIProxyAPIPlus', binaryPrefix: 'CLIProxyAPIPlus', executable: 'cli-proxy-api-plus', - fallbackVersion: '6.7.8-0', + fallbackVersion: '6.7.25-0', }, } as const; diff --git a/src/utils/websearch/hook-config.ts b/src/utils/websearch/hook-config.ts index 72ca82e5..d05003c0 100644 --- a/src/utils/websearch/hook-config.ts +++ b/src/utils/websearch/hook-config.ts @@ -13,12 +13,24 @@ import { info, warn } from '../ui'; import { getWebSearchConfig } from '../../config/unified-config-loader'; import { getCcsDir } from '../config-manager'; -// Path to Claude settings.json (intentionally uses real homedir for global settings) -const CLAUDE_SETTINGS_PATH = path.join(os.homedir(), '.claude', 'settings.json'); - // Hook file name const WEBSEARCH_HOOK = 'websearch-transformer.cjs'; +/** + * Get Claude settings path (respects CCS_HOME for test isolation) + * In tests, returns path under CCS_HOME; in production, uses real ~/.claude/ + */ +function getClaudeSettingsPath(): string { + const ccsHome = process.env.CCS_HOME; + if (ccsHome) { + // Test mode: use CCS_HOME parent for .claude directory + // This prevents tests from modifying user's real settings + return path.join(path.dirname(ccsHome), '.claude', 'settings.json'); + } + // Production: use real home directory + return path.join(os.homedir(), '.claude', 'settings.json'); +} + /** * Get CCS hooks directory (respects CCS_HOME for test isolation) */ @@ -92,9 +104,9 @@ export function ensureHookConfig(): boolean { // Read existing settings or start fresh let settings: Record = {}; - if (fs.existsSync(CLAUDE_SETTINGS_PATH)) { + if (fs.existsSync(getClaudeSettingsPath())) { try { - const content = fs.readFileSync(CLAUDE_SETTINGS_PATH, 'utf8'); + const content = fs.readFileSync(getClaudeSettingsPath(), 'utf8'); settings = JSON.parse(content); } catch { if (process.env.CCS_DEBUG) { @@ -136,7 +148,7 @@ export function ensureHookConfig(): boolean { } if (needsUpdate) { - fs.writeFileSync(CLAUDE_SETTINGS_PATH, JSON.stringify(settings, null, 2), 'utf8'); + fs.writeFileSync(getClaudeSettingsPath(), JSON.stringify(settings, null, 2), 'utf8'); if (process.env.CCS_DEBUG) { console.error(info('Updated WebSearch hook config in settings.json')); } @@ -163,13 +175,13 @@ export function ensureHookConfig(): boolean { settingsHooks.PreToolUse.push(...preToolUseHooks); // Ensure ~/.claude directory exists - const claudeDir = path.dirname(CLAUDE_SETTINGS_PATH); + const claudeDir = path.dirname(getClaudeSettingsPath()); if (!fs.existsSync(claudeDir)) { fs.mkdirSync(claudeDir, { recursive: true, mode: 0o700 }); } // Write updated settings - fs.writeFileSync(CLAUDE_SETTINGS_PATH, JSON.stringify(settings, null, 2), 'utf8'); + fs.writeFileSync(getClaudeSettingsPath(), JSON.stringify(settings, null, 2), 'utf8'); if (process.env.CCS_DEBUG) { console.error(info('Added WebSearch hook to settings.json')); @@ -191,11 +203,11 @@ export function ensureHookConfig(): boolean { */ export function removeHookConfig(): boolean { try { - if (!fs.existsSync(CLAUDE_SETTINGS_PATH)) { + if (!fs.existsSync(getClaudeSettingsPath())) { return true; // Nothing to remove } - const content = fs.readFileSync(CLAUDE_SETTINGS_PATH, 'utf8'); + const content = fs.readFileSync(getClaudeSettingsPath(), 'utf8'); let settings: Record; try { settings = JSON.parse(content); @@ -232,7 +244,7 @@ export function removeHookConfig(): boolean { delete settings.hooks; } - fs.writeFileSync(CLAUDE_SETTINGS_PATH, JSON.stringify(settings, null, 2), 'utf8'); + fs.writeFileSync(getClaudeSettingsPath(), JSON.stringify(settings, null, 2), 'utf8'); if (process.env.CCS_DEBUG) { console.error(info('Removed WebSearch hook from settings.json')); diff --git a/src/utils/websearch/hook-installer.ts b/src/utils/websearch/hook-installer.ts index 21da5a5f..16985043 100644 --- a/src/utils/websearch/hook-installer.ts +++ b/src/utils/websearch/hook-installer.ts @@ -10,7 +10,7 @@ import * as fs from 'fs'; import * as path from 'path'; import { info, warn } from '../ui'; import { getWebSearchConfig } from '../../config/unified-config-loader'; -import { getHookPath, ensureHookConfig, getCcsHooksDir } from './hook-config'; +import { getHookPath, getCcsHooksDir } from './hook-config'; import { removeMigrationMarker } from './profile-hook-injector'; // Re-export from hook-config for backward compatibility @@ -85,8 +85,9 @@ export function installWebSearchHook(): boolean { console.error(info(`Installed WebSearch hook: ${hookPath}`)); } - // Ensure hook is configured in settings.json - ensureHookConfig(); + // Note: Hook registration is handled by ensureProfileHooks() in profile-hook-injector.ts + // which writes to per-profile settings (~/.ccs/.settings.json) + // Global settings (~/.claude/settings.json) are NOT modified here return true; } catch (error) {