Skip to content

Commit ea5c0bc

Browse files
author
catlog22
committed
feat: Enhance CLI tools and settings management
- Added auto-initialization of CSRF token for state-changing requests in cli-manager.js. - Refactored Claude CLI Tools configuration to separate tools and settings into cli-tools.json and cli-settings.json respectively. - Introduced new interfaces for Claude CLI Tools and Settings, including support for tags and primary models. - Implemented loading and saving functions for CLI settings, ensuring backward compatibility with legacy combined config. - Updated functions to synchronize tags between CLI tools and configuration manager. - Added error handling and logging for loading and saving configurations. - Created initial cli-settings.json with default settings.
1 parent 0bd2cff commit ea5c0bc

12 files changed

Lines changed: 1377 additions & 181 deletions

File tree

.claude/cli-settings.json

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
{
2+
"version": "1.0.0",
3+
"defaultTool": "gemini",
4+
"promptFormat": "plain",
5+
"smartContext": {
6+
"enabled": false,
7+
"maxFiles": 10
8+
},
9+
"nativeResume": true,
10+
"recursiveQuery": true,
11+
"cache": {
12+
"injectionMode": "auto",
13+
"defaultPrefix": "",
14+
"defaultSuffix": ""
15+
},
16+
"codeIndexMcp": "ace",
17+
"$schema": "./cli-settings.schema.json"
18+
}

.claude/cli-tools.json

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,50 @@
11
{
2-
"version": "1.0.0",
2+
"version": "2.0.0",
33
"tools": {
44
"gemini": {
55
"enabled": true,
66
"isBuiltin": true,
77
"command": "gemini",
8-
"description": "Google AI for code analysis"
8+
"description": "Google AI for code analysis",
9+
"tags": []
910
},
1011
"qwen": {
1112
"enabled": true,
1213
"isBuiltin": true,
1314
"command": "qwen",
14-
"description": "Alibaba AI assistant"
15+
"description": "Alibaba AI assistant",
16+
"tags": []
1517
},
1618
"codex": {
1719
"enabled": true,
1820
"isBuiltin": true,
1921
"command": "codex",
20-
"description": "OpenAI code generation"
22+
"description": "OpenAI code generation",
23+
"tags": []
2124
},
2225
"claude": {
2326
"enabled": true,
2427
"isBuiltin": true,
2528
"command": "claude",
26-
"description": "Anthropic AI assistant"
29+
"description": "Anthropic AI assistant",
30+
"tags": []
2731
},
2832
"opencode": {
2933
"enabled": true,
3034
"isBuiltin": true,
3135
"command": "opencode",
3236
"description": "OpenCode AI assistant",
33-
"primaryModel": "opencode/glm-4.7-free"
37+
"primaryModel": "opencode/glm-4.7-free",
38+
"tags": []
3439
}
3540
},
36-
"customEndpoints": [],
41+
"customEndpoints": [
42+
{
43+
"id": "g25",
44+
"name": "g25",
45+
"enabled": true
46+
}
47+
],
3748
"defaultTool": "gemini",
3849
"settings": {
3950
"promptFormat": "plain",
@@ -48,7 +59,7 @@
4859
"defaultPrefix": "",
4960
"defaultSuffix": ""
5061
},
51-
"codeIndexMcp": "ace"
62+
"codeIndexMcp": "codexlens"
5263
},
5364
"$schema": "./cli-tools.schema.json"
5465
}

ccw/src/config/litellm-api-config-manager.ts

Lines changed: 214 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1033,5 +1033,219 @@ function objectToYaml(obj: unknown, indent: number = 0): string {
10331033
return String(obj);
10341034
}
10351035

1036+
// ===========================
1037+
// Multi-Model Pool Management
1038+
// ===========================
1039+
1040+
/**
1041+
* Migrate legacy embeddingPoolConfig to new modelPools array
1042+
* This function ensures backward compatibility with existing configurations
1043+
*/
1044+
function migrateEmbeddingPoolToModelPools(config: LiteLLMApiConfig): void {
1045+
// Skip if already has modelPools or no legacy config
1046+
if (config.modelPools && config.modelPools.length > 0) return;
1047+
if (!config.embeddingPoolConfig) return;
1048+
1049+
// Convert legacy embeddingPoolConfig to ModelPoolConfig
1050+
const legacyPool = config.embeddingPoolConfig;
1051+
const modelPool: import('../types/litellm-api-config.js').ModelPoolConfig = {
1052+
id: `pool-embedding-${Date.now()}`,
1053+
modelType: 'embedding',
1054+
enabled: legacyPool.enabled,
1055+
targetModel: legacyPool.targetModel,
1056+
strategy: legacyPool.strategy,
1057+
autoDiscover: legacyPool.autoDiscover,
1058+
excludedProviderIds: legacyPool.excludedProviderIds || [],
1059+
defaultCooldown: legacyPool.defaultCooldown,
1060+
defaultMaxConcurrentPerKey: legacyPool.defaultMaxConcurrentPerKey,
1061+
name: `Embedding Pool - ${legacyPool.targetModel}`,
1062+
description: 'Migrated from legacy embeddingPoolConfig',
1063+
};
1064+
1065+
config.modelPools = [modelPool];
1066+
// Keep legacy config for backward compatibility with old CodexLens versions
1067+
}
1068+
1069+
/**
1070+
* Get all model pool configurations
1071+
* Returns empty array if no pools configured
1072+
*/
1073+
export function getModelPools(baseDir: string): import('../types/litellm-api-config.js').ModelPoolConfig[] {
1074+
const config = loadLiteLLMApiConfig(baseDir);
1075+
1076+
// Auto-migrate if needed
1077+
migrateEmbeddingPoolToModelPools(config);
1078+
1079+
return config.modelPools || [];
1080+
}
1081+
1082+
/**
1083+
* Get a specific model pool by ID
1084+
*/
1085+
export function getModelPool(
1086+
baseDir: string,
1087+
poolId: string
1088+
): import('../types/litellm-api-config.js').ModelPoolConfig | undefined {
1089+
const pools = getModelPools(baseDir);
1090+
return pools.find(p => p.id === poolId);
1091+
}
1092+
1093+
/**
1094+
* Add a new model pool configuration
1095+
*/
1096+
export function addModelPool(
1097+
baseDir: string,
1098+
poolConfig: Omit<import('../types/litellm-api-config.js').ModelPoolConfig, 'id'>
1099+
): { poolId: string; syncResult?: { success: boolean; message: string; endpointCount?: number } } {
1100+
const config = loadLiteLLMApiConfig(baseDir);
1101+
1102+
// Auto-migrate if needed
1103+
migrateEmbeddingPoolToModelPools(config);
1104+
1105+
// Ensure modelPools array exists
1106+
if (!config.modelPools) {
1107+
config.modelPools = [];
1108+
}
1109+
1110+
// Generate unique ID
1111+
const poolId = `pool-${poolConfig.modelType}-${Date.now()}`;
1112+
1113+
const newPool: import('../types/litellm-api-config.js').ModelPoolConfig = {
1114+
...poolConfig,
1115+
id: poolId,
1116+
};
1117+
1118+
config.modelPools.push(newPool);
1119+
saveConfig(baseDir, config);
1120+
1121+
// Sync to CodexLens if this is an embedding pool
1122+
const syncResult = poolConfig.modelType === 'embedding' && poolConfig.enabled
1123+
? syncCodexLensConfig(baseDir)
1124+
: undefined;
1125+
1126+
return { poolId, syncResult };
1127+
}
1128+
1129+
/**
1130+
* Update an existing model pool configuration
1131+
*/
1132+
export function updateModelPool(
1133+
baseDir: string,
1134+
poolId: string,
1135+
updates: Partial<Omit<import('../types/litellm-api-config.js').ModelPoolConfig, 'id'>>
1136+
): { success: boolean; syncResult?: { success: boolean; message: string; endpointCount?: number } } {
1137+
const config = loadLiteLLMApiConfig(baseDir);
1138+
1139+
// Auto-migrate if needed
1140+
migrateEmbeddingPoolToModelPools(config);
1141+
1142+
if (!config.modelPools) {
1143+
return { success: false };
1144+
}
1145+
1146+
const poolIndex = config.modelPools.findIndex(p => p.id === poolId);
1147+
if (poolIndex === -1) {
1148+
return { success: false };
1149+
}
1150+
1151+
// Apply updates
1152+
config.modelPools[poolIndex] = {
1153+
...config.modelPools[poolIndex],
1154+
...updates,
1155+
};
1156+
1157+
saveConfig(baseDir, config);
1158+
1159+
// Sync to CodexLens if this is an enabled embedding pool
1160+
const pool = config.modelPools[poolIndex];
1161+
const syncResult = pool.modelType === 'embedding' && pool.enabled
1162+
? syncCodexLensConfig(baseDir)
1163+
: undefined;
1164+
1165+
return { success: true, syncResult };
1166+
}
1167+
1168+
/**
1169+
* Delete a model pool configuration
1170+
*/
1171+
export function deleteModelPool(
1172+
baseDir: string,
1173+
poolId: string
1174+
): { success: boolean; syncResult?: { success: boolean; message: string; endpointCount?: number } } {
1175+
const config = loadLiteLLMApiConfig(baseDir);
1176+
1177+
if (!config.modelPools) {
1178+
return { success: false };
1179+
}
1180+
1181+
const poolIndex = config.modelPools.findIndex(p => p.id === poolId);
1182+
if (poolIndex === -1) {
1183+
return { success: false };
1184+
}
1185+
1186+
const deletedPool = config.modelPools[poolIndex];
1187+
config.modelPools.splice(poolIndex, 1);
1188+
1189+
saveConfig(baseDir, config);
1190+
1191+
// Sync to CodexLens if we deleted an embedding pool
1192+
const syncResult = deletedPool.modelType === 'embedding'
1193+
? syncCodexLensConfig(baseDir)
1194+
: undefined;
1195+
1196+
return { success: true, syncResult };
1197+
}
1198+
1199+
/**
1200+
* Get available models for a specific model type
1201+
* Used for pool configuration UI
1202+
*/
1203+
export function getAvailableModelsForType(
1204+
baseDir: string,
1205+
modelType: import('../types/litellm-api-config.js').ModelPoolType
1206+
): Array<{ modelId: string; modelName: string; providers: string[] }> {
1207+
const config = loadLiteLLMApiConfig(baseDir);
1208+
const availableModels: Array<{ modelId: string; modelName: string; providers: string[] }> = [];
1209+
const modelMap = new Map<string, { modelId: string; modelName: string; providers: string[] }>();
1210+
1211+
for (const provider of config.providers) {
1212+
if (!provider.enabled) continue;
1213+
1214+
let models: typeof provider.embeddingModels | undefined;
1215+
1216+
switch (modelType) {
1217+
case 'embedding':
1218+
models = provider.embeddingModels;
1219+
break;
1220+
case 'llm':
1221+
models = provider.llmModels;
1222+
break;
1223+
case 'reranker':
1224+
models = provider.rerankerModels;
1225+
break;
1226+
}
1227+
1228+
if (!models) continue;
1229+
1230+
for (const model of models) {
1231+
if (!model.enabled) continue;
1232+
1233+
const key = model.id;
1234+
if (modelMap.has(key)) {
1235+
modelMap.get(key)!.providers.push(provider.name);
1236+
} else {
1237+
modelMap.set(key, {
1238+
modelId: model.id,
1239+
modelName: model.name,
1240+
providers: [provider.name],
1241+
});
1242+
}
1243+
}
1244+
}
1245+
1246+
availableModels.push(...Array.from(modelMap.values()));
1247+
return availableModels;
1248+
}
1249+
10361250
// Re-export types
10371251
export type { ProviderCredential, CustomEndpoint, ProviderType, CacheStrategy, CodexLensEmbeddingRotation, CodexLensEmbeddingProvider, EmbeddingPoolConfig };

0 commit comments

Comments
 (0)