mirror of
https://github.com/tiennm99/ccs.git
synced 2026-10-14 14:17:11 +00:00
feat(cliproxy): add user settings override for model customization
- Add getEffectiveEnvVars() to read user overrides from ~/.ccs/{provider}.settings.json
- Add ensureProviderSettings() to create settings file from defaults on first run
- Update cliproxy-executor to use effective env vars with override support
- Export new functions from index.ts
Users can now customize model mappings by editing ~/.ccs/gemini.settings.json
or ~/.ccs/codex.settings.json. This enables:
- Using gemini-3-pro-preview for all model types
- Easy migration when new models release (gemini 4, 5, etc.)
- User takes full responsibility for custom settings
Default behavior unchanged - works out of box with bundled defaults.
This commit is contained in:
1 parent
efb966c96d
commit
50d32ca7da
3 files changed
+90
-7
No files matched your search
@@ -19,8 +19,9 @@ import { escapeShellArg } from '../utils/shell-executor';
|
||||
import { ensureCLIProxyBinary } from './binary-manager';
|
||||
import {
|
||||
generateConfig,
|
||||
getClaudeEnvVars,
|
||||
getEffectiveEnvVars,
|
||||
getProviderConfig,
|
||||
ensureProviderSettings,
|
||||
CLIPROXY_DEFAULT_PORT,
|
||||
} from './config-generator';
|
||||
import { isAuthenticated } from './auth-handler';
|
||||
@@ -154,12 +155,15 @@ export async function execClaudeWithCLIProxy(
|
||||
}
|
||||
}
|
||||
|
||||
// 4. Generate config file
|
||||
// 4. Ensure user settings file exists (creates from defaults if not)
|
||||
ensureProviderSettings(provider);
|
||||
|
||||
// 5. Generate config file
|
||||
log(`Generating config for ${provider}`);
|
||||
const configPath = generateConfig(provider, cfg.port);
|
||||
log(`Config written: ${configPath}`);
|
||||
|
||||
// 5. Spawn CLIProxyAPI binary
|
||||
// 6. Spawn CLIProxyAPI binary
|
||||
const proxyArgs = ['--config', configPath];
|
||||
|
||||
log(`Spawning: ${binaryPath} ${proxyArgs.join(' ')}`);
|
||||
@@ -213,8 +217,9 @@ export async function execClaudeWithCLIProxy(
|
||||
throw new Error(`CLIProxy startup failed: ${err.message}`);
|
||||
}
|
||||
|
||||
// 6. Execute Claude CLI with proxied environment
|
||||
const envVars = getClaudeEnvVars(provider, cfg.port);
|
||||
// 7. Execute Claude CLI with proxied environment
|
||||
// Uses user settings if ~/.ccs/{provider}.settings.json exists, else bundled defaults
|
||||
const envVars = getEffectiveEnvVars(provider, cfg.port);
|
||||
const env = { ...process.env, ...envVars };
|
||||
|
||||
log(`Claude env: ANTHROPIC_BASE_URL=${envVars.ANTHROPIC_BASE_URL}`);
|
||||
@@ -244,7 +249,7 @@ export async function execClaudeWithCLIProxy(
|
||||
});
|
||||
}
|
||||
|
||||
// 7. Cleanup: kill proxy when Claude exits
|
||||
// 8. Cleanup: kill proxy when Claude exits
|
||||
claude.on('exit', (code, signal) => {
|
||||
log(`Claude exited: code=${code}, signal=${signal}`);
|
||||
proxy.kill('SIGTERM');
|
||||
|
||||
@@ -10,6 +10,11 @@ import * as path from 'path';
|
||||
import { getCcsDir } from '../utils/config-manager';
|
||||
import { CLIProxyProvider, ProviderConfig, ProviderModelMapping } from './types';
|
||||
|
||||
/** Settings file structure for user overrides */
|
||||
interface ProviderSettings {
|
||||
env: NodeJS.ProcessEnv;
|
||||
}
|
||||
|
||||
/** Default CLIProxy port */
|
||||
export const CLIPROXY_DEFAULT_PORT = 8317;
|
||||
|
||||
@@ -193,7 +198,15 @@ export function deleteConfig(): void {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get environment variables for Claude CLI
|
||||
* Get path to user settings file for provider
|
||||
* Example: ~/.ccs/gemini.settings.json
|
||||
*/
|
||||
export function getProviderSettingsPath(provider: CLIProxyProvider): string {
|
||||
return path.join(getCcsDir(), `${provider}.settings.json`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get environment variables for Claude CLI (bundled defaults)
|
||||
* Uses provider-specific endpoint (e.g., /api/provider/gemini) for explicit routing.
|
||||
* This enables concurrent gemini/codex usage - each session routes to its provider via URL path.
|
||||
*/
|
||||
@@ -213,3 +226,65 @@ export function getClaudeEnvVars(
|
||||
ANTHROPIC_DEFAULT_HAIKU_MODEL: models.haikuModel || models.claudeModel,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get effective environment variables for provider
|
||||
*
|
||||
* Priority order:
|
||||
* 1. User settings file (~/.ccs/{provider}.settings.json) if exists
|
||||
* 2. Bundled defaults from PROVIDER_CONFIGS
|
||||
*
|
||||
* This allows users to customize model mappings without code changes.
|
||||
* User takes full responsibility for custom settings.
|
||||
*/
|
||||
export function getEffectiveEnvVars(
|
||||
provider: CLIProxyProvider,
|
||||
port: number = CLIPROXY_DEFAULT_PORT
|
||||
): NodeJS.ProcessEnv {
|
||||
const settingsPath = getProviderSettingsPath(provider);
|
||||
|
||||
// Check for user override file
|
||||
if (fs.existsSync(settingsPath)) {
|
||||
try {
|
||||
const content = fs.readFileSync(settingsPath, 'utf-8');
|
||||
const settings: ProviderSettings = JSON.parse(content);
|
||||
|
||||
if (settings.env && typeof settings.env === 'object') {
|
||||
// User override found - use their settings
|
||||
// Note: User is responsible for correctness
|
||||
return settings.env;
|
||||
}
|
||||
} catch {
|
||||
// Invalid JSON or structure - fall through to defaults
|
||||
// Silent fallback: don't spam errors for broken user files
|
||||
}
|
||||
}
|
||||
|
||||
// No override or invalid - use bundled defaults
|
||||
return getClaudeEnvVars(provider, port);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy bundled settings template to user directory if not exists
|
||||
* Called during installation/first run
|
||||
*/
|
||||
export function ensureProviderSettings(provider: CLIProxyProvider): void {
|
||||
const settingsPath = getProviderSettingsPath(provider);
|
||||
|
||||
// Only create if doesn't exist (preserve user edits)
|
||||
if (fs.existsSync(settingsPath)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Generate default settings from PROVIDER_CONFIGS
|
||||
const envVars = getClaudeEnvVars(provider);
|
||||
const settings: ProviderSettings = { env: envVars };
|
||||
|
||||
// Ensure directory exists
|
||||
fs.mkdirSync(path.dirname(settingsPath), { recursive: true });
|
||||
|
||||
// Write with restricted permissions
|
||||
fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + '\n', {
|
||||
mode: 0o600,
|
||||
});
|
||||
}
|
||||
@@ -46,6 +46,9 @@ export {
|
||||
export {
|
||||
generateConfig,
|
||||
getClaudeEnvVars,
|
||||
getEffectiveEnvVars,
|
||||
getProviderSettingsPath,
|
||||
ensureProviderSettings,
|
||||
getProviderConfig,
|
||||
getModelMapping,
|
||||
getCliproxyDir,
|
||||
|
||||
Reference in new issue
Block a user