mirror of
https://github.com/Wei-Shaw/claude-relay-service.git
synced 2026-01-22 16:43:35 +00:00
fix:修复1.1.147版本启动问题
This commit is contained in:
69
src/validators/clientDefinitions.js
Normal file
69
src/validators/clientDefinitions.js
Normal file
@@ -0,0 +1,69 @@
|
||||
/**
|
||||
* 客户端定义配置
|
||||
* 定义所有支持的客户端类型和它们的属性
|
||||
*/
|
||||
|
||||
const CLIENT_DEFINITIONS = {
|
||||
CLAUDE_CODE: {
|
||||
id: 'claude_code',
|
||||
name: 'Claude Code',
|
||||
displayName: 'Claude Code CLI',
|
||||
description: 'Claude Code command-line interface',
|
||||
userAgentPattern: /^claude-cli\/[\d.]+([-\w]*)?\s+\(external,\s*cli\)$/i,
|
||||
requiredHeaders: ['x-app', 'anthropic-beta', 'anthropic-version'],
|
||||
restrictedPaths: ['/api/v1/messages', '/claude/v1/messages'],
|
||||
icon: '🤖'
|
||||
},
|
||||
|
||||
GEMINI_CLI: {
|
||||
id: 'gemini_cli',
|
||||
name: 'Gemini CLI',
|
||||
displayName: 'Gemini Command Line Tool',
|
||||
description: 'Google Gemini API command-line interface',
|
||||
userAgentPattern: /^GeminiCLI\/v?[\d.]+/i,
|
||||
requiredPaths: ['/gemini'],
|
||||
validatePaths: ['generateContent'],
|
||||
icon: '💎'
|
||||
},
|
||||
|
||||
CODEX_CLI: {
|
||||
id: 'codex_cli',
|
||||
name: 'Codex CLI',
|
||||
displayName: 'Codex Command Line Tool',
|
||||
description: 'Cursor/Codex command-line interface',
|
||||
userAgentPattern: /^(codex_vscode|codex_cli_rs)\/[\d.]+/i,
|
||||
requiredHeaders: ['originator', 'session_id'],
|
||||
restrictedPaths: ['/openai', '/azure'],
|
||||
icon: '🔷'
|
||||
}
|
||||
}
|
||||
|
||||
// 导出客户端ID枚举
|
||||
const CLIENT_IDS = {
|
||||
CLAUDE_CODE: 'claude_code',
|
||||
GEMINI_CLI: 'gemini_cli',
|
||||
CODEX_CLI: 'codex_cli'
|
||||
}
|
||||
|
||||
// 获取所有客户端定义
|
||||
function getAllClientDefinitions() {
|
||||
return Object.values(CLIENT_DEFINITIONS)
|
||||
}
|
||||
|
||||
// 根据ID获取客户端定义
|
||||
function getClientDefinitionById(clientId) {
|
||||
return Object.values(CLIENT_DEFINITIONS).find((client) => client.id === clientId)
|
||||
}
|
||||
|
||||
// 检查客户端ID是否有效
|
||||
function isValidClientId(clientId) {
|
||||
return Object.values(CLIENT_IDS).includes(clientId)
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
CLIENT_DEFINITIONS,
|
||||
CLIENT_IDS,
|
||||
getAllClientDefinitions,
|
||||
getClientDefinitionById,
|
||||
isValidClientId
|
||||
}
|
||||
143
src/validators/clientValidator.js
Normal file
143
src/validators/clientValidator.js
Normal file
@@ -0,0 +1,143 @@
|
||||
/**
|
||||
* 客户端验证器
|
||||
* 用于验证请求是否来自特定的客户端
|
||||
*/
|
||||
|
||||
const logger = require('../utils/logger')
|
||||
const { CLIENT_DEFINITIONS, getAllClientDefinitions } = require('./clientDefinitions')
|
||||
const ClaudeCodeValidator = require('./clients/claudeCodeValidator')
|
||||
const GeminiCliValidator = require('./clients/geminiCliValidator')
|
||||
const CodexCliValidator = require('./clients/codexCliValidator')
|
||||
|
||||
/**
|
||||
* 客户端验证器类
|
||||
*/
|
||||
class ClientValidator {
|
||||
/**
|
||||
* 获取客户端验证器
|
||||
* @param {string} clientId - 客户端ID
|
||||
* @returns {Object|null} 验证器实例
|
||||
*/
|
||||
static getValidator(clientId) {
|
||||
switch (clientId) {
|
||||
case 'claude_code':
|
||||
return ClaudeCodeValidator
|
||||
case 'gemini_cli':
|
||||
return GeminiCliValidator
|
||||
case 'codex_cli':
|
||||
return CodexCliValidator
|
||||
default:
|
||||
logger.warn(`Unknown client ID: ${clientId}`)
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有支持的客户端ID列表
|
||||
* @returns {Array<string>} 客户端ID列表
|
||||
*/
|
||||
static getSupportedClients() {
|
||||
return ['claude_code', 'gemini_cli', 'codex_cli']
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证单个客户端
|
||||
* @param {string} clientId - 客户端ID
|
||||
* @param {Object} req - Express请求对象
|
||||
* @returns {boolean} 验证结果
|
||||
*/
|
||||
static validateClient(clientId, req) {
|
||||
const validator = this.getValidator(clientId)
|
||||
|
||||
if (!validator) {
|
||||
logger.warn(`No validator found for client: ${clientId}`)
|
||||
return false
|
||||
}
|
||||
|
||||
try {
|
||||
return validator.validate(req)
|
||||
} catch (error) {
|
||||
logger.error(`Error validating client ${clientId}:`, error)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证请求是否来自允许的客户端列表中的任一客户端
|
||||
* @param {Array<string>} allowedClients - 允许的客户端ID列表
|
||||
* @param {Object} req - Express请求对象
|
||||
* @returns {Object} 验证结果对象
|
||||
*/
|
||||
static validateRequest(allowedClients, req) {
|
||||
const userAgent = req.headers['user-agent'] || ''
|
||||
const clientIP = req.ip || req.connection?.remoteAddress || 'unknown'
|
||||
|
||||
// 记录验证开始
|
||||
logger.api(`🔍 Starting client validation for User-Agent: "${userAgent}"`)
|
||||
logger.api(` Allowed clients: ${allowedClients.join(', ')}`)
|
||||
logger.api(` Request from IP: ${clientIP}`)
|
||||
|
||||
// 遍历所有允许的客户端进行验证
|
||||
for (const clientId of allowedClients) {
|
||||
const validator = this.getValidator(clientId)
|
||||
|
||||
if (!validator) {
|
||||
logger.warn(`Skipping unknown client ID: ${clientId}`)
|
||||
continue
|
||||
}
|
||||
|
||||
logger.debug(`Checking against ${validator.getName()}...`)
|
||||
|
||||
try {
|
||||
if (validator.validate(req)) {
|
||||
// 验证成功
|
||||
logger.api(`✅ Client validated: ${validator.getName()} (${clientId})`)
|
||||
logger.api(` Matched User-Agent: "${userAgent}"`)
|
||||
|
||||
return {
|
||||
allowed: true,
|
||||
matchedClient: clientId,
|
||||
clientName: validator.getName(),
|
||||
clientInfo: Object.values(CLIENT_DEFINITIONS).find((def) => def.id === clientId)
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error(`Error during validation for ${clientId}:`, error)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// 没有匹配的客户端
|
||||
logger.api(`❌ No matching client found for User-Agent: "${userAgent}"`)
|
||||
return {
|
||||
allowed: false,
|
||||
matchedClient: null,
|
||||
reason: 'No matching client found'
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取客户端信息
|
||||
* @param {string} clientId - 客户端ID
|
||||
* @returns {Object} 客户端信息
|
||||
*/
|
||||
static getClientInfo(clientId) {
|
||||
const validator = this.getValidator(clientId)
|
||||
if (!validator) {
|
||||
return null
|
||||
}
|
||||
|
||||
return validator.getInfo()
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有可用的客户端信息
|
||||
* @returns {Array<Object>} 客户端信息数组
|
||||
*/
|
||||
static getAvailableClients() {
|
||||
// 直接从 CLIENT_DEFINITIONS 返回所有客户端信息
|
||||
return getAllClientDefinitions()
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ClientValidator
|
||||
140
src/validators/clients/claudeCodeValidator.js
Normal file
140
src/validators/clients/claudeCodeValidator.js
Normal file
@@ -0,0 +1,140 @@
|
||||
const logger = require('../../utils/logger')
|
||||
const { CLIENT_DEFINITIONS } = require('../clientDefinitions')
|
||||
|
||||
/**
|
||||
* Claude Code CLI 验证器
|
||||
* 验证请求是否来自 Claude Code CLI
|
||||
*/
|
||||
class ClaudeCodeValidator {
|
||||
/**
|
||||
* 获取客户端ID
|
||||
*/
|
||||
static getId() {
|
||||
return CLIENT_DEFINITIONS.CLAUDE_CODE.id
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取客户端名称
|
||||
*/
|
||||
static getName() {
|
||||
return CLIENT_DEFINITIONS.CLAUDE_CODE.name
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取客户端描述
|
||||
*/
|
||||
static getDescription() {
|
||||
return CLIENT_DEFINITIONS.CLAUDE_CODE.description
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取客户端图标
|
||||
*/
|
||||
static getIcon() {
|
||||
return CLIENT_DEFINITIONS.CLAUDE_CODE.icon || '🤖'
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证请求是否来自 Claude Code CLI
|
||||
* @param {Object} req - Express 请求对象
|
||||
* @returns {boolean} 验证结果
|
||||
*/
|
||||
static validate(req) {
|
||||
try {
|
||||
const userAgent = req.headers['user-agent'] || ''
|
||||
const path = req.path || ''
|
||||
|
||||
// 1. 先检查是否是 Claude Code 的 User-Agent
|
||||
// 格式: claude-cli/1.0.86 (external, cli)
|
||||
const claudeCodePattern = /^claude-cli\/[\d\.]+([-\w]*)?\s+\(external,\s*cli\)$/i
|
||||
if (!claudeCodePattern.test(userAgent)) {
|
||||
// 不是 Claude Code 的请求,此验证器不处理
|
||||
return false
|
||||
}
|
||||
|
||||
// 2. Claude Code 检测到,对于特定路径进行额外的严格验证
|
||||
if (!path.includes('messages')) {
|
||||
// 其他路径,只要 User-Agent 匹配就认为是 Claude Code
|
||||
logger.debug(`Claude Code detected for path: ${path}, allowing access`)
|
||||
return true
|
||||
}
|
||||
|
||||
// 3. 检查必需的头部(值不为空即可)
|
||||
const xApp = req.headers['x-app']
|
||||
const anthropicBeta = req.headers['anthropic-beta']
|
||||
const anthropicVersion = req.headers['anthropic-version']
|
||||
|
||||
if (!xApp || xApp.trim() === '') {
|
||||
logger.debug('Claude Code validation failed - missing or empty x-app header')
|
||||
return false
|
||||
}
|
||||
|
||||
if (!anthropicBeta || anthropicBeta.trim() === '') {
|
||||
logger.debug('Claude Code validation failed - missing or empty anthropic-beta header')
|
||||
return false
|
||||
}
|
||||
|
||||
if (!anthropicVersion || anthropicVersion.trim() === '') {
|
||||
logger.debug('Claude Code validation failed - missing or empty anthropic-version header')
|
||||
return false
|
||||
}
|
||||
|
||||
logger.debug(`Claude Code headers - x-app: ${xApp}, anthropic-beta: ${anthropicBeta}, anthropic-version: ${anthropicVersion}`)
|
||||
|
||||
// 4. 验证 body 中的 metadata.user_id
|
||||
if (!req.body || !req.body.metadata || !req.body.metadata.user_id) {
|
||||
logger.debug('Claude Code validation failed - missing metadata.user_id in body')
|
||||
return false
|
||||
}
|
||||
|
||||
const userId = req.body.metadata.user_id
|
||||
// 格式: user_{64位字符串}_account__session_{哈希值}
|
||||
// user_d98385411c93cd074b2cefd5c9831fe77f24a53e4ecdcd1f830bba586fe62cb9_account__session_17cf0fd3-d51b-4b59-977d-b899dafb3022
|
||||
const userIdPattern = /^user_[a-fA-F0-9]{64}_account__session_[\w-]+$/
|
||||
|
||||
if (!userIdPattern.test(userId)) {
|
||||
logger.debug(`Claude Code validation failed - invalid user_id format: ${userId}`)
|
||||
|
||||
// 提供更详细的错误信息
|
||||
if (!userId.startsWith('user_')) {
|
||||
logger.debug('user_id must start with "user_"')
|
||||
} else {
|
||||
const parts = userId.split('_')
|
||||
if (parts.length < 4) {
|
||||
logger.debug('user_id format is incomplete')
|
||||
} else if (parts[1].length !== 64) {
|
||||
logger.debug(`user hash must be 64 characters, got ${parts[1].length}`)
|
||||
} else if (parts[2] !== 'account' || parts[3] !== '' || parts[4] !== 'session') {
|
||||
logger.debug('user_id must contain "_account__session_"')
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// 5. 额外日志记录(用于调试)
|
||||
logger.debug(`Claude Code validation passed - UA: ${userAgent}, userId: ${userId}`)
|
||||
|
||||
// 所有必要检查通过
|
||||
return true
|
||||
|
||||
} catch (error) {
|
||||
logger.error('Error in ClaudeCodeValidator:', error)
|
||||
// 验证出错时默认拒绝
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取验证器信息
|
||||
*/
|
||||
static getInfo() {
|
||||
return {
|
||||
id: this.getId(),
|
||||
name: this.getName(),
|
||||
description: this.getDescription(),
|
||||
icon: CLIENT_DEFINITIONS.CLAUDE_CODE.icon
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ClaudeCodeValidator
|
||||
147
src/validators/clients/codexCliValidator.js
Normal file
147
src/validators/clients/codexCliValidator.js
Normal file
@@ -0,0 +1,147 @@
|
||||
const logger = require('../../utils/logger')
|
||||
const { CLIENT_DEFINITIONS } = require('../clientDefinitions')
|
||||
|
||||
/**
|
||||
* Codex CLI 验证器
|
||||
* 验证请求是否来自 Codex CLI
|
||||
*/
|
||||
class CodexCliValidator {
|
||||
/**
|
||||
* 获取客户端ID
|
||||
*/
|
||||
static getId() {
|
||||
return CLIENT_DEFINITIONS.CODEX_CLI.id
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取客户端名称
|
||||
*/
|
||||
static getName() {
|
||||
return CLIENT_DEFINITIONS.CODEX_CLI.name
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取客户端描述
|
||||
*/
|
||||
static getDescription() {
|
||||
return CLIENT_DEFINITIONS.CODEX_CLI.description
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证请求是否来自 Codex CLI
|
||||
* @param {Object} req - Express 请求对象
|
||||
* @returns {boolean} 验证结果
|
||||
*/
|
||||
static validate(req) {
|
||||
try {
|
||||
const userAgent = req.headers['user-agent'] || ''
|
||||
const originator = req.headers['originator'] || ''
|
||||
const sessionId = req.headers['session_id']
|
||||
|
||||
// 1. 基础 User-Agent 检查
|
||||
// Codex CLI 的 UA 格式:
|
||||
// - codex_vscode/0.35.0 (Windows 10.0.26100; x86_64) unknown (Cursor; 0.4.10)
|
||||
// - codex_cli_rs/0.38.0 (Ubuntu 22.4.0; x86_64) WindowsTerminal
|
||||
const codexCliPattern = /^(codex_vscode|codex_cli_rs)\/[\d\.]+/i
|
||||
const uaMatch = userAgent.match(codexCliPattern)
|
||||
|
||||
if (!uaMatch) {
|
||||
logger.debug(`Codex CLI validation failed - UA mismatch: ${userAgent}`)
|
||||
return false
|
||||
}
|
||||
|
||||
// 2. 对于特定路径,进行额外的严格验证
|
||||
// 对于 /openai 和 /azure 路径需要完整验证
|
||||
const strictValidationPaths = ['/openai', '/azure']
|
||||
const needsStrictValidation = req.path && strictValidationPaths.some(path => req.path.startsWith(path))
|
||||
|
||||
if (!needsStrictValidation) {
|
||||
// 其他路径,只要 User-Agent 匹配就认为是 Codex CLI
|
||||
logger.debug(`Codex CLI detected for path: ${req.path}, allowing access`)
|
||||
return true
|
||||
}
|
||||
|
||||
// 3. 验证 originator 头必须与 UA 中的客户端类型匹配
|
||||
const clientType = uaMatch[1].toLowerCase()
|
||||
if (originator.toLowerCase() !== clientType) {
|
||||
logger.debug(
|
||||
`Codex CLI validation failed - originator mismatch. UA: ${clientType}, originator: ${originator}`
|
||||
)
|
||||
return false
|
||||
}
|
||||
|
||||
// 4. 检查 session_id - 必须存在且长度大于20
|
||||
if (!sessionId || sessionId.length <= 20) {
|
||||
logger.debug(`Codex CLI validation failed - session_id missing or too short: ${sessionId}`)
|
||||
return false
|
||||
}
|
||||
|
||||
// 5. 对于 /openai/responses 和 /azure/response 路径,额外检查 body 中的 instructions 字段
|
||||
if (
|
||||
req.path &&
|
||||
(req.path.includes('/openai/responses') || req.path.includes('/azure/response'))
|
||||
) {
|
||||
if (!req.body || !req.body.instructions) {
|
||||
logger.debug(`Codex CLI validation failed - missing instructions in body for ${req.path}`)
|
||||
return false
|
||||
}
|
||||
|
||||
const expectedPrefix =
|
||||
'You are Codex, based on GPT-5. You are running as a coding agent in the Codex CLI'
|
||||
if (!req.body.instructions.startsWith(expectedPrefix)) {
|
||||
logger.debug(`Codex CLI validation failed - invalid instructions prefix for ${req.path}`)
|
||||
logger.debug(`Expected: "${expectedPrefix}..."`)
|
||||
logger.debug(`Received: "${req.body.instructions.substring(0, 100)}..."`)
|
||||
return false
|
||||
}
|
||||
|
||||
// 额外检查 model 字段应该是 gpt-5-codex
|
||||
if (req.body.model && req.body.model !== 'gpt-5-codex') {
|
||||
logger.debug(`Codex CLI validation warning - unexpected model: ${req.body.model}`)
|
||||
// 只记录警告,不拒绝请求
|
||||
}
|
||||
}
|
||||
|
||||
// 所有必要检查通过
|
||||
logger.debug(`Codex CLI validation passed for UA: ${userAgent}`)
|
||||
return true
|
||||
} catch (error) {
|
||||
logger.error('Error in CodexCliValidator:', error)
|
||||
// 验证出错时默认拒绝
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 比较版本号
|
||||
* @returns {number} -1: v1 < v2, 0: v1 = v2, 1: v1 > v2
|
||||
*/
|
||||
static compareVersions(v1, v2) {
|
||||
const parts1 = v1.split('.').map(Number)
|
||||
const parts2 = v2.split('.').map(Number)
|
||||
|
||||
for (let i = 0; i < Math.max(parts1.length, parts2.length); i++) {
|
||||
const part1 = parts1[i] || 0
|
||||
const part2 = parts2[i] || 0
|
||||
|
||||
if (part1 < part2) return -1
|
||||
if (part1 > part2) return 1
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取验证器信息
|
||||
*/
|
||||
static getInfo() {
|
||||
return {
|
||||
id: this.getId(),
|
||||
name: this.getName(),
|
||||
description: this.getDescription(),
|
||||
icon: CLIENT_DEFINITIONS.CODEX_CLI.icon
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = CodexCliValidator
|
||||
105
src/validators/clients/geminiCliValidator.js
Normal file
105
src/validators/clients/geminiCliValidator.js
Normal file
@@ -0,0 +1,105 @@
|
||||
const logger = require('../../utils/logger')
|
||||
const { CLIENT_DEFINITIONS } = require('../clientDefinitions')
|
||||
|
||||
/**
|
||||
* Gemini CLI 验证器
|
||||
* 验证请求是否来自 Gemini CLI
|
||||
*/
|
||||
class GeminiCliValidator {
|
||||
/**
|
||||
* 获取客户端ID
|
||||
*/
|
||||
static getId() {
|
||||
return CLIENT_DEFINITIONS.GEMINI_CLI.id
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取客户端名称
|
||||
*/
|
||||
static getName() {
|
||||
return CLIENT_DEFINITIONS.GEMINI_CLI.name
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取客户端描述
|
||||
*/
|
||||
static getDescription() {
|
||||
return CLIENT_DEFINITIONS.GEMINI_CLI.description
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取客户端图标
|
||||
*/
|
||||
static getIcon() {
|
||||
return CLIENT_DEFINITIONS.GEMINI_CLI.icon || '💎'
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证请求是否来自 Gemini CLI
|
||||
* @param {Object} req - Express 请求对象
|
||||
* @returns {boolean} 验证结果
|
||||
*/
|
||||
static validate(req) {
|
||||
try {
|
||||
const userAgent = req.headers['user-agent'] || ''
|
||||
const path = req.originalUrl || ''
|
||||
|
||||
// 1. 必须是 /gemini 开头的路径
|
||||
if (!path.startsWith('/gemini')) {
|
||||
// 非 /gemini 路径不属于 Gemini
|
||||
return false
|
||||
}
|
||||
|
||||
// 2. 对于 /gemini 路径,检查是否包含 generateContent
|
||||
if (path.includes('generateContent')) {
|
||||
// 包含 generateContent 的路径需要验证 User-Agent
|
||||
const geminiCliPattern = /^GeminiCLI\/v?[\d\.]+/i
|
||||
if (!geminiCliPattern.test(userAgent)) {
|
||||
logger.debug(`Gemini CLI validation failed - UA mismatch for generateContent: ${userAgent}`)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// 所有必要检查通过
|
||||
logger.debug(`Gemini CLI validation passed for path: ${path}`)
|
||||
return true
|
||||
} catch (error) {
|
||||
logger.error('Error in GeminiCliValidator:', error)
|
||||
// 验证出错时默认拒绝
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 比较版本号
|
||||
* @returns {number} -1: v1 < v2, 0: v1 = v2, 1: v1 > v2
|
||||
*/
|
||||
static compareVersions(v1, v2) {
|
||||
const parts1 = v1.split('.').map(Number)
|
||||
const parts2 = v2.split('.').map(Number)
|
||||
|
||||
for (let i = 0; i < Math.max(parts1.length, parts2.length); i++) {
|
||||
const part1 = parts1[i] || 0
|
||||
const part2 = parts2[i] || 0
|
||||
|
||||
if (part1 < part2) return -1
|
||||
if (part1 > part2) return 1
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取验证器信息
|
||||
*/
|
||||
static getInfo() {
|
||||
return {
|
||||
id: this.getId(),
|
||||
name: this.getName(),
|
||||
description: this.getDescription(),
|
||||
icon: CLIENT_DEFINITIONS.GEMINI_CLI.icon
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = GeminiCliValidator
|
||||
Reference in New Issue
Block a user