Files
ccs/src/cliproxy/sync/profile-mapper.ts
T
kaitranntt 32dbd5e174 refactor(cliproxy): remove model alias functionality
- delete model-alias-config.ts and cliproxy-alias-handler.ts

- simplify sync to use ANTHROPIC_MODEL directly

- remove alias routes, hooks, and UI components
2026-01-28 11:57:22 -05:00

190 lines
4.5 KiB
TypeScript

/**
* Profile Mapper for CLIProxy Sync
*
* Transforms CCS settings-based profiles into CLIProxy ClaudeKey format.
*/
import * as fs from 'fs';
import * as path from 'path';
import { getCcsDir } from '../../utils/config-manager';
import { listApiProfiles, isApiProfileConfigured } from '../../api/services/profile-reader';
import type { ClaudeKey } from '../management-api-types';
/**
* Profile info with settings for sync.
*/
export interface SyncableProfile {
/** Profile name (e.g., "glm", "kimi") */
name: string;
/** Path to settings.json file */
settingsPath: string;
/** Whether profile has valid API key */
isConfigured: boolean;
/** Environment variables from settings.json */
env?: Record<string, string>;
}
/**
* Settings.json file structure (Claude compatible).
*/
interface SettingsJson {
env?: Record<string, string>;
}
/**
* Load syncable API profiles from CCS config.
* Filters to only configured profiles (with real API keys).
*/
export function loadSyncableProfiles(): SyncableProfile[] {
const { profiles } = listApiProfiles();
const syncable: SyncableProfile[] = [];
for (const profile of profiles) {
// Skip unconfigured profiles
if (!profile.isConfigured) {
continue;
}
// Load settings.json for env vars
const ccsDir = getCcsDir();
const settingsPath = path.join(ccsDir, `${profile.name}.settings.json`);
let env: Record<string, string> | undefined;
try {
if (fs.existsSync(settingsPath)) {
const content = fs.readFileSync(settingsPath, 'utf8');
const settings = JSON.parse(content) as SettingsJson;
env = settings.env;
}
} catch {
// Skip profiles with unreadable settings
continue;
}
// Must have ANTHROPIC_AUTH_TOKEN
const token = env?.ANTHROPIC_AUTH_TOKEN;
if (!token || token.includes('YOUR_') || token.includes('your-')) {
continue;
}
syncable.push({
name: profile.name,
settingsPath,
isConfigured: true,
env,
});
}
return syncable;
}
/**
* Sanitize profile name for YAML safety.
* Replaces non-alphanumeric chars (except - and _) with hyphens.
*/
function sanitizeProfileName(name: string): string {
return name.replace(/[^a-zA-Z0-9-_]/g, '-');
}
/**
* Map a single profile to ClaudeKey format.
*/
export function mapProfileToClaudeKey(profile: SyncableProfile): ClaudeKey | null {
const env = profile.env;
if (!env) return null;
const apiKey = env.ANTHROPIC_AUTH_TOKEN;
if (!apiKey) return null;
const baseUrl = env.ANTHROPIC_BASE_URL;
const modelName = env.ANTHROPIC_MODEL;
// Generate prefix from profile name (e.g., "glm" -> "glm-")
const sanitizedName = sanitizeProfileName(profile.name);
if (!sanitizedName || sanitizedName === '') {
return null; // Skip profiles with invalid names
}
const prefix = `${sanitizedName}-`;
const claudeKey: ClaudeKey = {
'api-key': apiKey,
prefix,
};
if (baseUrl) {
claudeKey['base-url'] = baseUrl;
}
// Use model name directly from profile (no alias mapping)
if (modelName) {
claudeKey.models = [
{
name: modelName,
alias: '',
},
];
}
return claudeKey;
}
/**
* Generate sync payload from all configured profiles.
* Returns array of ClaudeKey ready to push to remote CLIProxy.
*/
export function generateSyncPayload(): ClaudeKey[] {
const profiles = loadSyncableProfiles();
const keys: ClaudeKey[] = [];
for (const profile of profiles) {
const key = mapProfileToClaudeKey(profile);
if (key) {
keys.push(key);
}
}
return keys;
}
/**
* Generate sync preview with profile details.
* Used for dry-run mode to show what would be synced.
*/
export interface SyncPreviewItem {
/** Profile name */
name: string;
/** Base URL (masked) */
baseUrl?: string;
/** Model name */
modelName?: string;
}
export function generateSyncPreview(): SyncPreviewItem[] {
const profiles = loadSyncableProfiles();
const preview: SyncPreviewItem[] = [];
for (const profile of profiles) {
preview.push({
name: profile.name,
baseUrl: profile.env?.ANTHROPIC_BASE_URL,
modelName: profile.env?.ANTHROPIC_MODEL,
});
}
return preview;
}
/**
* Get count of syncable profiles.
*/
export function getSyncableProfileCount(): number {
return loadSyncableProfiles().length;
}
/**
* Check if profile is syncable (configured with valid API key).
*/
export function isProfileSyncable(profileName: string): boolean {
return isApiProfileConfigured(profileName);
}