2025-08-07 20:51:13 +08:00
|
|
|
|
import fs from "fs/promises";
|
|
|
|
|
|
import path from "path";
|
|
|
|
|
|
import os from "os";
|
|
|
|
|
|
import { Provider } from "../shared/types";
|
2025-08-04 22:16:26 +08:00
|
|
|
|
|
2025-08-07 15:48:30 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 清理供应商名称,确保文件名安全
|
|
|
|
|
|
*/
|
|
|
|
|
|
export function sanitizeProviderName(name: string): string {
|
2025-08-07 20:51:13 +08:00
|
|
|
|
return name.replace(/[<>:"/\\|?*]/g, "-").toLowerCase();
|
2025-08-06 07:42:57 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-08-04 22:16:26 +08:00
|
|
|
|
export function getClaudeCodeConfig() {
|
|
|
|
|
|
// Claude Code 配置文件路径
|
2025-08-07 20:51:13 +08:00
|
|
|
|
const configDir = path.join(os.homedir(), ".claude");
|
|
|
|
|
|
const configPath = path.join(configDir, "settings.json");
|
2025-08-05 20:10:51 +08:00
|
|
|
|
|
2025-08-07 20:51:13 +08:00
|
|
|
|
return { path: configPath, dir: configDir };
|
2025-08-04 22:16:26 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-08-07 15:48:30 +08:00
|
|
|
|
/**
|
2025-08-07 20:51:13 +08:00
|
|
|
|
* 获取供应商配置文件路径(基于供应商名称)
|
2025-08-07 15:48:30 +08:00
|
|
|
|
*/
|
2025-08-07 20:51:13 +08:00
|
|
|
|
export function getProviderConfigPath(
|
|
|
|
|
|
providerId: string,
|
|
|
|
|
|
providerName?: string
|
|
|
|
|
|
): string {
|
|
|
|
|
|
const { dir } = getClaudeCodeConfig();
|
|
|
|
|
|
|
|
|
|
|
|
// 如果提供了名称,使用名称;否则使用ID(向后兼容)
|
|
|
|
|
|
const baseName = providerName
|
|
|
|
|
|
? sanitizeProviderName(providerName)
|
|
|
|
|
|
: sanitizeProviderName(providerId);
|
|
|
|
|
|
return path.join(dir, `settings-${baseName}.json`);
|
2025-08-07 15:48:30 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 保存供应商配置到独立文件
|
|
|
|
|
|
*/
|
|
|
|
|
|
export async function saveProviderConfig(provider: Provider): Promise<boolean> {
|
|
|
|
|
|
try {
|
2025-08-07 20:51:13 +08:00
|
|
|
|
const { dir } = getClaudeCodeConfig();
|
|
|
|
|
|
const providerConfigPath = getProviderConfigPath(
|
|
|
|
|
|
provider.id,
|
|
|
|
|
|
provider.name
|
|
|
|
|
|
);
|
|
|
|
|
|
|
2025-08-07 15:48:30 +08:00
|
|
|
|
// 确保目录存在
|
2025-08-07 20:51:13 +08:00
|
|
|
|
await fs.mkdir(dir, { recursive: true });
|
|
|
|
|
|
|
2025-08-07 15:48:30 +08:00
|
|
|
|
// 保存配置到供应商专用文件
|
|
|
|
|
|
await fs.writeFile(
|
|
|
|
|
|
providerConfigPath,
|
|
|
|
|
|
JSON.stringify(provider.settingsConfig, null, 2),
|
2025-08-07 20:51:13 +08:00
|
|
|
|
"utf-8"
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
return true;
|
2025-08-07 15:48:30 +08:00
|
|
|
|
} catch (error) {
|
2025-08-07 20:51:13 +08:00
|
|
|
|
console.error("保存供应商配置失败:", error);
|
|
|
|
|
|
return false;
|
2025-08-07 15:48:30 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 检查文件是否存在
|
|
|
|
|
|
*/
|
|
|
|
|
|
export async function fileExists(filePath: string): Promise<boolean> {
|
2025-08-04 22:16:26 +08:00
|
|
|
|
try {
|
2025-08-07 20:51:13 +08:00
|
|
|
|
await fs.access(filePath);
|
|
|
|
|
|
return true;
|
2025-08-07 15:48:30 +08:00
|
|
|
|
} catch {
|
2025-08-07 20:51:13 +08:00
|
|
|
|
return false;
|
2025-08-07 15:48:30 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-08-05 20:10:51 +08:00
|
|
|
|
|
2025-08-07 15:48:30 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 切换供应商配置(基于文件重命名)
|
|
|
|
|
|
*/
|
2025-08-07 20:51:13 +08:00
|
|
|
|
export async function switchProvider(
|
|
|
|
|
|
provider: Provider,
|
|
|
|
|
|
currentProviderId?: string,
|
|
|
|
|
|
providers?: Record<string, Provider>
|
|
|
|
|
|
): Promise<boolean> {
|
2025-08-07 15:48:30 +08:00
|
|
|
|
try {
|
2025-08-07 20:51:13 +08:00
|
|
|
|
const { path: settingsPath, dir: configDir } = getClaudeCodeConfig();
|
|
|
|
|
|
const newSettingsPath = getProviderConfigPath(provider.id, provider.name);
|
|
|
|
|
|
|
2025-08-04 22:16:26 +08:00
|
|
|
|
// 确保目录存在
|
2025-08-07 20:51:13 +08:00
|
|
|
|
await fs.mkdir(configDir, { recursive: true });
|
|
|
|
|
|
|
2025-08-07 15:48:30 +08:00
|
|
|
|
// 检查目标配置文件是否存在
|
|
|
|
|
|
if (!(await fileExists(newSettingsPath))) {
|
2025-08-07 20:51:13 +08:00
|
|
|
|
console.error(`供应商配置文件不存在: ${newSettingsPath}`);
|
|
|
|
|
|
return false;
|
2025-08-04 22:16:26 +08:00
|
|
|
|
}
|
2025-08-07 20:51:13 +08:00
|
|
|
|
|
2025-08-07 15:48:30 +08:00
|
|
|
|
// 1. 如果当前存在settings.json,先备份到当前供应商的配置文件
|
|
|
|
|
|
if (await fileExists(settingsPath)) {
|
2025-08-07 20:51:13 +08:00
|
|
|
|
if (currentProviderId && providers && providers[currentProviderId]) {
|
|
|
|
|
|
const currentProvider = providers[currentProviderId];
|
|
|
|
|
|
const currentProviderPath = getProviderConfigPath(
|
|
|
|
|
|
currentProviderId,
|
|
|
|
|
|
currentProvider.name
|
|
|
|
|
|
);
|
|
|
|
|
|
await fs.rename(settingsPath, currentProviderPath);
|
2025-08-07 15:48:30 +08:00
|
|
|
|
} else {
|
|
|
|
|
|
// 如果没有当前供应商ID,创建临时备份
|
2025-08-07 20:51:13 +08:00
|
|
|
|
const backupPath = path.join(
|
|
|
|
|
|
configDir,
|
|
|
|
|
|
`settings-backup-${Date.now()}.json`
|
|
|
|
|
|
);
|
|
|
|
|
|
await fs.rename(settingsPath, backupPath);
|
|
|
|
|
|
console.log(`已备份当前配置到: ${backupPath}`);
|
2025-08-07 15:48:30 +08:00
|
|
|
|
}
|
2025-08-04 22:16:26 +08:00
|
|
|
|
}
|
2025-08-07 20:51:13 +08:00
|
|
|
|
|
2025-08-07 15:48:30 +08:00
|
|
|
|
// 2. 将目标供应商配置重命名为settings.json
|
2025-08-07 20:51:13 +08:00
|
|
|
|
await fs.rename(newSettingsPath, settingsPath);
|
|
|
|
|
|
|
|
|
|
|
|
console.log(`成功切换到供应商: ${provider.name}`);
|
|
|
|
|
|
return true;
|
2025-08-07 15:48:30 +08:00
|
|
|
|
} catch (error) {
|
2025-08-07 20:51:13 +08:00
|
|
|
|
console.error("切换供应商失败:", error);
|
|
|
|
|
|
return false;
|
2025-08-07 15:48:30 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-08-05 20:10:51 +08:00
|
|
|
|
|
2025-08-07 15:48:30 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 导入当前 settings.json 配置为一个供应商
|
|
|
|
|
|
*/
|
2025-08-07 20:51:13 +08:00
|
|
|
|
export async function importCurrentConfig(
|
|
|
|
|
|
name: string
|
|
|
|
|
|
): Promise<{ success: boolean; provider?: Provider }> {
|
2025-08-07 15:48:30 +08:00
|
|
|
|
try {
|
2025-08-07 20:51:13 +08:00
|
|
|
|
const { path: settingsPath } = getClaudeCodeConfig();
|
|
|
|
|
|
|
2025-08-07 15:48:30 +08:00
|
|
|
|
// 检查当前配置是否存在
|
|
|
|
|
|
if (!(await fileExists(settingsPath))) {
|
2025-08-07 20:51:13 +08:00
|
|
|
|
return { success: false };
|
2025-08-07 15:48:30 +08:00
|
|
|
|
}
|
2025-08-07 20:51:13 +08:00
|
|
|
|
|
2025-08-07 15:48:30 +08:00
|
|
|
|
// 读取当前配置
|
2025-08-07 20:51:13 +08:00
|
|
|
|
const configContent = await fs.readFile(settingsPath, "utf-8");
|
|
|
|
|
|
const settingsConfig = JSON.parse(configContent);
|
|
|
|
|
|
|
2025-08-07 15:48:30 +08:00
|
|
|
|
// 生成唯一的供应商ID
|
2025-08-07 20:51:13 +08:00
|
|
|
|
let providerId = name.toLowerCase().replace(/[^a-z0-9]/g, "-");
|
|
|
|
|
|
let counter = 1;
|
|
|
|
|
|
|
2025-08-07 15:48:30 +08:00
|
|
|
|
// 检查ID是否已存在,如果存在则添加数字后缀
|
2025-08-07 20:51:13 +08:00
|
|
|
|
while (await fileExists(getProviderConfigPath(providerId, name))) {
|
|
|
|
|
|
providerId = `${name
|
|
|
|
|
|
.toLowerCase()
|
|
|
|
|
|
.replace(/[^a-z0-9]/g, "-")}-${counter}`;
|
|
|
|
|
|
counter++;
|
2025-08-07 15:48:30 +08:00
|
|
|
|
}
|
2025-08-07 20:51:13 +08:00
|
|
|
|
|
2025-08-07 15:48:30 +08:00
|
|
|
|
// 创建供应商对象
|
|
|
|
|
|
const provider: Provider = {
|
|
|
|
|
|
id: providerId,
|
|
|
|
|
|
name: name,
|
|
|
|
|
|
settingsConfig: settingsConfig,
|
|
|
|
|
|
createdAt: Date.now(),
|
2025-08-07 20:51:13 +08:00
|
|
|
|
updatedAt: Date.now(),
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2025-08-07 15:48:30 +08:00
|
|
|
|
// 保存为供应商配置
|
2025-08-07 20:51:13 +08:00
|
|
|
|
const success = await saveProviderConfig(provider);
|
|
|
|
|
|
|
2025-08-07 15:48:30 +08:00
|
|
|
|
if (success) {
|
2025-08-07 20:51:13 +08:00
|
|
|
|
console.log(`已导入当前配置为供应商: ${name} (${providerId})`);
|
|
|
|
|
|
return { success: true, provider };
|
2025-08-07 15:48:30 +08:00
|
|
|
|
} else {
|
2025-08-07 20:51:13 +08:00
|
|
|
|
return { success: false };
|
2025-08-07 15:48:30 +08:00
|
|
|
|
}
|
|
|
|
|
|
} catch (error: any) {
|
2025-08-07 20:51:13 +08:00
|
|
|
|
console.error("导入当前配置失败:", error);
|
|
|
|
|
|
return { success: false };
|
2025-08-07 15:48:30 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-08-05 20:10:51 +08:00
|
|
|
|
|
2025-08-07 15:48:30 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 删除供应商配置文件
|
|
|
|
|
|
*/
|
2025-08-07 20:51:13 +08:00
|
|
|
|
export async function deleteProviderConfig(
|
|
|
|
|
|
providerId: string,
|
|
|
|
|
|
providerName?: string
|
|
|
|
|
|
): Promise<boolean> {
|
2025-08-07 15:48:30 +08:00
|
|
|
|
try {
|
2025-08-07 20:51:13 +08:00
|
|
|
|
const providerConfigPath = getProviderConfigPath(providerId, providerName);
|
|
|
|
|
|
|
2025-08-07 15:48:30 +08:00
|
|
|
|
if (await fileExists(providerConfigPath)) {
|
2025-08-07 20:51:13 +08:00
|
|
|
|
await fs.unlink(providerConfigPath);
|
|
|
|
|
|
console.log(`已删除供应商配置文件: ${providerConfigPath}`);
|
2025-08-07 15:48:30 +08:00
|
|
|
|
}
|
2025-08-07 20:51:13 +08:00
|
|
|
|
|
|
|
|
|
|
return true;
|
2025-08-04 22:16:26 +08:00
|
|
|
|
} catch (error) {
|
2025-08-07 20:51:13 +08:00
|
|
|
|
console.error("删除供应商配置失败:", error);
|
|
|
|
|
|
return false;
|
2025-08-04 22:16:26 +08:00
|
|
|
|
}
|
2025-08-07 20:51:13 +08:00
|
|
|
|
}
|