ccr-model-manager
Version:
A command-line tool for managing Claude Code Router (CCR) model configurations with interactive selection
402 lines (396 loc) • 14.4 kB
JavaScript
const require_select = require('./select-BTG1tPDJ.js');
let child_process = require("child_process");
child_process = require_select.__toESM(child_process);
let chalk = require("chalk");
chalk = require_select.__toESM(chalk);
let commander = require("commander");
commander = require_select.__toESM(commander);
//#region src/commands/list.ts
/**
* List命令实现
*/
var ListCommand = class {
/**
* 执行list命令
*/
static async execute() {
try {
require_select.FormatUtils.showLoading(require_select.UI_MESSAGES.READING_CONFIG);
const config = await require_select.ConfigService.getConfig();
const providers = await require_select.ConfigService.getActiveProviders();
if (providers.length === 0) {
require_select.FormatUtils.showError(require_select.ERROR_MESSAGES.NO_PROVIDERS);
return;
}
this.displayProviders(providers);
require_select.FormatUtils.displayCurrentSelection(config);
} catch (error) {
this.handleError(error);
}
}
/**
* 显示提供商列表和模型
* @param providers 提供商列表
*/
static displayProviders(providers) {
require_select.FormatUtils.displayProviderList(providers);
}
/**
* 显示简化版的提供商信息(带统计)
* @param providers 提供商列表
*/
static displayProvidersWithStats(providers) {
console.log(require_select.FormatUtils.formatInfo("可用的模型提供商统计:"));
console.log("");
if (providers.length === 0) {
require_select.FormatUtils.showError("未找到任何提供商");
return;
}
const totalProviders = providers.length;
const totalModels = providers.reduce((sum, provider) => sum + (provider.models?.length || 0), 0);
const deprecatedCount = providers.filter((p) => p.deprecated).length;
const activeCount = totalProviders - deprecatedCount;
console.log(require_select.FormatUtils.formatInfo(`总提供商数: ${totalProviders}`));
console.log(require_select.FormatUtils.formatSuccess(`活跃提供商: ${activeCount}`));
console.log(require_select.FormatUtils.formatWarning(`已弃用: ${deprecatedCount}`));
console.log(require_select.FormatUtils.formatInfo(`总模型数: ${totalModels}`));
console.log("");
providers.forEach((provider, index) => {
const status = provider.deprecated ? "🟡 已弃用" : "🟢 活跃";
const statusColor = provider.deprecated ? "yellow" : "green";
const modelCount = provider.models?.length || 0;
console.log(`${index + 1}. ${chalk.default[statusColor](provider.name)} - ${status}`);
console.log(` 模型数量: ${modelCount}`);
if (provider.models && provider.models.length > 0) console.log(` 可用模型: ${provider.models.slice(0, 3).join(", ")}${provider.models.length > 3 ? "..." : ""}`);
if (provider.api_base_url) console.log(` API地址: ${provider.api_base_url}`);
console.log("");
});
}
/**
* 搜索提供商或模型
* @param query 搜索查询字符串
*/
static async search(query) {
try {
require_select.FormatUtils.showLoading(`正在搜索 "${query}"...`);
const providers = await require_select.ConfigService.getProviders(true);
const lowerQuery = query.toLowerCase();
const matchingProviders = providers.filter((provider) => provider.name.toLowerCase().includes(lowerQuery));
const matchingModels = [];
providers.forEach((provider) => {
if (provider.models) provider.models.forEach((model) => {
if (model.toLowerCase().includes(lowerQuery)) matchingModels.push({
provider,
model
});
});
});
this.displaySearchResults(matchingProviders, matchingModels, query);
} catch (error) {
this.handleError(error);
}
}
/**
* 显示搜索结果
* @param matchingProviders 匹配的提供商
* @param matchingModels 匹配的模型
* @param query 搜索查询
*/
static displaySearchResults(matchingProviders, matchingModels, query) {
console.log(require_select.FormatUtils.formatInfo(`搜索结果 for "${query}":`));
console.log("");
if (matchingProviders.length > 0) {
console.log(require_select.FormatUtils.formatSuccess(`匹配的提供商 (${matchingProviders.length}):`));
matchingProviders.forEach((provider) => {
const status = provider.deprecated ? "🟡 已弃用" : "🟢 活跃";
const statusColor = provider.deprecated ? "yellow" : "green";
console.log(` - ${chalk.default[statusColor](provider.name)} - ${status}`);
});
console.log("");
}
if (matchingModels.length > 0) {
console.log(require_select.FormatUtils.formatSuccess(`匹配的模型 (${matchingModels.length}):`));
const modelsByProvider = matchingModels.reduce((acc, { provider, model }) => {
if (!acc[provider.name]) acc[provider.name] = [];
acc[provider.name].push(model);
return acc;
}, {});
Object.entries(modelsByProvider).forEach(([providerName, models]) => {
const statusColor = (matchingModels.find((m) => m.provider.name === providerName)?.provider)?.deprecated ? "yellow" : "green";
console.log(` ${chalk.default[statusColor](providerName)}:`);
models.forEach((model) => {
console.log(` - ${model}`);
});
});
console.log("");
}
if (matchingProviders.length === 0 && matchingModels.length === 0) require_select.FormatUtils.showWarning(`未找到与 "${query}" 匹配的提供商或模型`);
}
/**
* 导出提供商列表为JSON
* @param outputPath 输出文件路径(可选)
*/
static async exportToJson(outputPath) {
try {
require_select.FormatUtils.showLoading("正在导出提供商数据...");
const providers = await require_select.ConfigService.getProviders(true);
const exportData = {
exportedAt: (/* @__PURE__ */ new Date()).toISOString(),
totalProviders: providers.length,
activeProviders: providers.filter((p) => !p.deprecated).length,
providers: providers.map((provider) => ({
name: provider.name,
status: provider.deprecated ? "deprecated" : "active",
apiBaseUrl: provider.api_base_url,
models: provider.models || [],
modelCount: provider.models?.length || 0
}))
};
const jsonData = JSON.stringify(exportData, null, 2);
if (outputPath) {
await (await import("fs-extra")).writeFile(outputPath, jsonData, "utf8");
require_select.FormatUtils.showSuccess(`提供商数据已导出到: ${outputPath}`);
} else console.log(jsonData);
} catch (error) {
this.handleError(error);
}
}
/**
* 统一错误处理
* @param error 错误对象
*/
static handleError(error) {
if (error instanceof Error) require_select.FormatUtils.showError(`发生错误: ${error.message}`);
else require_select.FormatUtils.showError("发生未知错误");
console.error(error);
}
};
//#endregion
//#region src/commands/routers.ts
/**
* Routers命令实现
*/
var RoutersCommand = class {
/**
* 执行routers命令
*/
static async execute() {
try {
require_select.FormatUtils.showLoading(require_select.UI_MESSAGES.READING_CONFIG);
const config = await require_select.ConfigService.getConfig();
const providers = await require_select.ConfigService.getProviders(true);
if (!require_select.ValidatorService.isConfigUsable(config)) {
require_select.FormatUtils.showError(require_select.ERROR_MESSAGES.NO_ROUTER_CONFIG);
return;
}
require_select.FormatUtils.displayRouterTable(config, providers);
} catch (error) {
this.handleError(error);
}
}
/**
* 处理错误
* @param error 错误对象
*/
static handleError(error) {
if (error instanceof Error) require_select.FormatUtils.showError(`发生错误: ${error.message}`);
else require_select.FormatUtils.showError("发生未知错误");
console.error(error);
}
};
//#endregion
//#region src/services/updater.ts
/**
* 基于npm的更新服务类
*/
var UpdateService = class {
static {
this.PACKAGE_NAME = "ccr-model-manager";
}
/**
* 获取当前版本
* @returns Promise<string> 当前版本
*/
static async getCurrentVersion() {
try {
const versionMatch = (0, child_process.execSync)("cmm --version", {
encoding: "utf8",
timeout: 5e3
}).trim().match(/(\d+\.\d+\.\d+)/);
if (versionMatch) return versionMatch[1];
throw new Error("无法解析版本号");
} catch (error) {
try {
const packagePath = require.resolve("../package.json");
return require(packagePath).version;
} catch {
throw new Error("无法获取当前版本信息");
}
}
}
/**
* 从npm获取最新版本
* @returns Promise<string> 最新版本
*/
static async getLatestVersionFromNpm() {
try {
const output = (0, child_process.execSync)(`npm view ${this.PACKAGE_NAME} version --json`, {
encoding: "utf8",
timeout: 1e4
}).trim();
return JSON.parse(output);
} catch (error) {
if (error instanceof Error && error.message.includes("404")) throw new Error(`包 "${this.PACKAGE_NAME}" 在npm上不存在`);
throw new Error("无法从npm获取版本信息");
}
}
/**
* 比较版本号
* @param current 当前版本
* @param latest 最新版本
* @returns boolean 是否有更新
*/
static isNewer(current, latest) {
const currentParts = current.split(".").map(Number);
const latestParts = latest.split(".").map(Number);
for (let i = 0; i < Math.max(currentParts.length, latestParts.length); i++) {
const currentPart = currentParts[i] || 0;
const latestPart = latestParts[i] || 0;
if (latestPart > currentPart) return true;
if (latestPart < currentPart) return false;
}
return false;
}
/**
* 检查更新
* @returns Promise<UpdateInfo> 更新信息
*/
static async checkForUpdates() {
try {
require_select.FormatUtils.showLoading("正在检查更新...");
const currentVersion = await this.getCurrentVersion();
const latestVersion = await this.getLatestVersionFromNpm();
const updateAvailable = this.isNewer(currentVersion, latestVersion);
return {
currentVersion,
latestVersion,
updateAvailable
};
} catch (error) {
if (error instanceof Error) throw new Error(`检查更新失败: ${error.message}`);
throw new Error("检查更新失败: 未知错误");
}
}
/**
* 执行更新
* @returns Promise<UpdateResult> 更新结果
*/
static async performUpdate() {
try {
const updateInfo = await this.checkForUpdates();
if (!updateInfo.updateAvailable) return {
success: true,
message: "当前已是最新版本",
currentVersion: updateInfo.currentVersion,
newVersion: updateInfo.currentVersion
};
console.log(require_select.FormatUtils.formatInfo("发现新版本!"));
console.log(`${require_select.FormatUtils.formatSuccess("当前版本:")} ${updateInfo.currentVersion}`);
console.log(`${require_select.FormatUtils.formatWarning("最新版本:")} ${updateInfo.latestVersion}`);
console.log("");
require_select.FormatUtils.showLoading("正在从npm更新...");
try {
(0, child_process.execSync)(`npm install -g ${this.PACKAGE_NAME}@latest`, {
stdio: "inherit",
timeout: 3e5
});
return {
success: true,
message: `更新成功: ${updateInfo.currentVersion} -> ${updateInfo.latestVersion}`,
currentVersion: updateInfo.currentVersion,
newVersion: updateInfo.latestVersion
};
} catch (npmError) {
if (npmError instanceof Error && npmError.message.includes("EACCES")) return {
success: false,
message: "权限不足,请使用管理员权限运行更新",
error: "需要管理员权限",
suggestion: `请运行: sudo npm install -g ${this.PACKAGE_NAME}@latest`
};
if (npmError instanceof Error) return {
success: false,
message: `npm安装失败: ${npmError.message}`,
error: npmError.message
};
return {
success: false,
message: "npm安装失败",
error: "未知npm错误"
};
}
} catch (error) {
if (error instanceof Error) return {
success: false,
message: `更新失败: ${error.message}`,
error: error.message
};
return {
success: false,
message: "更新失败: 未知错误"
};
}
}
};
//#endregion
//#region src/commands/update.ts
/**
* Update命令实现 - 基于npm的简洁版本
*/
var UpdateCommand = class {
/**
* 执行update命令 - 检查并更新到最新版本
*/
static async execute() {
try {
const result = await UpdateService.performUpdate();
if (result.success) {
require_select.FormatUtils.showSuccess(`🎉 ${result.message}`);
if (result.currentVersion && result.newVersion && result.currentVersion !== result.newVersion) {
console.log(`${require_select.FormatUtils.formatInfo("从:")} ${result.currentVersion}`);
console.log(`${require_select.FormatUtils.formatSuccess("到:")} ${result.newVersion}`);
console.log("");
console.log(require_select.FormatUtils.formatInfo("请重新启动命令行工具以使用新版本。"));
}
} else {
require_select.FormatUtils.showError(`❌ ${result.message}`);
if ("suggestion" in result) console.log(require_select.FormatUtils.formatWarning(`建议: ${result.suggestion}`));
if (result.error) console.log(require_select.FormatUtils.formatInfo(`错误详情: ${result.error}`));
}
} catch (error) {
if (error instanceof Error) if (error.message.includes("网络")) require_select.FormatUtils.showError("网络错误,请检查网络连接后重试。");
else if (error.message.includes("npm")) require_select.FormatUtils.showError("npm操作失败,请确保npm已正确安装。");
else require_select.FormatUtils.showError(`更新失败: ${error.message}`);
else require_select.FormatUtils.showError("更新失败:未知错误");
console.error(error);
}
}
};
//#endregion
//#region src/index.ts
const program = new commander.Command();
program.name("cmm").description("CCR模型管理器").version("1.3.0");
program.command("select").description("选择CCR模型提供商和模型ID").action(async () => {
await require_select.SelectCommand.execute();
});
program.command("list").description("列出所有可用的模型提供商和模型ID").action(async () => {
await ListCommand.execute();
});
program.command("routers").description("查看当前CCR所有router对应的模型信息").action(async () => {
await RoutersCommand.execute();
});
program.command("update").description("更新ccr-model-manager到最新版本").action(async () => {
await UpdateCommand.execute();
});
program.parse();
//#endregion