๐ crypto-util.ts โข 22026 bytes
/**
* CmdCode V0.5 - ๅ ๅฏๅญๅจ็ณป็ป
*
* ๐ ๅฎๅ
จๅๅ๏ผ
* - ๅฏ้ฅๆฐธไธๅจไปฃ็ ไธญ็กฌ็ผ็
* - ๅฏ้ฅๆฐธไธๅจ็ฏๅขๅ้ไธญไผ ้
* - ๅฏ้ฅๆฐธไธๅจๅฝไปค่กๅๆฐไธญๅบ็ฐ
* - ๅฏ้ฅๆฐธไธๅจๆฅๅฟๆไปถไธญ่ฎฐๅฝ
* - ๅฏ้ฅไป
ๅจๅ
ๅญไธญ่งฃๅฏ๏ผ็จๅฎๅณ็
*/
import { createCipheriv, createDecipheriv, randomBytes, createHash } from 'node:crypto'
import { readFileSync, writeFileSync, existsSync, mkdirSync, unlinkSync, chmodSync } from 'node:fs'
import { join } from 'node:path'
import { homedir } from 'node:os'
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
// ้
็ฝฎๅธธ้
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
const SECRETS_FILE = join(homedir(), '.cmdcode', 'secrets.enc')
const CMD_DIR = join(homedir(), '.cmdcode')
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
// ็ฑปๅๅฎไน
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
/** ๅฏ้ฅๆฑ ๆก็ฎ๏ผๅญๅจๅฝขๅผ๏ผ */
export interface KeyPoolEntry {
name: string
model: string
baseUrl: string
apiKeyEncrypted: string // ๅ ๅฏๅ็ API Key
}
/** ๆๆ้
็ฝฎ็ปๆ */
export interface Secrets {
// ็จๆท่ชๅฎไนๅฏ้ฅ๏ผๅๅฏ้ฅๆจกๅผ๏ผ
apiKey?: string
model?: string
baseUrl?: string
// ๅฏ้ฅๆฑ ๏ผๅคๅฏ้ฅๆจกๅผ๏ผ
chatKeyPool?: KeyPoolEntry[]
embeddingKeyPool?: KeyPoolEntry[]
// ๅ้่ฎฐๅฟ็ณป็ป้
็ฝฎ
memorySearch?: {
provider: string
model: string
baseUrl: string
}
// QQ Bot
qqBotApiKey?: string
qqBotApiSecret?: string
// ็จๆท็ปๅฝ
userToken?: string
userInfo?: { username: string; workspaceDir: string; quota?: number }
// ๅ
ถไป
[key: string]: any
}
export interface KeyStore { apiKey: string; baseUrl: string }
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
// ไธปๅฏ้ฅ็ฎก็
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
/** ๆฏๅฆไฝฟ็จไบ fallback ไธปๅฏ้ฅ๏ผๅฎๅ
จ่ญฆๅ๏ผ */
let usingFallbackKey = false
/** ็จๆท่ฎพ็ฝฎ็ไธปๅฏ็ ๏ผไฝไธบ้ขๅค็ตๆบ๏ผ */
let userPassphrase: string | null = null
/** ๆฃๆฅๆฏๅฆไฝฟ็จไบไธๅฎๅ
จ็ไธปๅฏ้ฅ */
export function isUsingFallbackKey(): boolean {
return usingFallbackKey
}
/** ่ฎพ็ฝฎ็จๆทไธปๅฏ็ ๏ผๅขๅผบๅฎๅ
จๆง๏ผ */
export function setMasterPassphrase(passphrase: string): void {
userPassphrase = passphrase
// ่ฎพ็ฝฎๅๆธ
้ค fallback ่ญฆๅ
usingFallbackKey = false
}
/** ่ทๅๅ ๅฏๅฏ้ฅ - ไปๆบๅจIDๆดพ็ */
function getEncryptionKey(): Buffer {
let machineId = ''
let entropySource = ''
try {
machineId = readFileSync('/etc/machine-id', 'utf-8').trim()
entropySource = machineId
} catch {
// machine-id ไธๅญๅจ
entropySource = ''
}
// ๅฆๆ็จๆท่ฎพ็ฝฎไบไธปๅฏ็ ๏ผไฝฟ็จไธปๅฏ็ ไฝไธบไธป่ฆ็ตๆบ
if (userPassphrase && userPassphrase.length >= 8) {
return createHash('sha256')
.update(userPassphrase + '-cmdcode-secrets-v2')
.digest()
}
// ๅฆๆ machine-id ๅฏ็จ๏ผไฝฟ็จๆบๅจIDๆดพ็
if (entropySource) {
return createHash('sha256')
.update(entropySource + '-cmdcode-secrets')
.digest()
}
// โ ๏ธ ๅฑ้ฉ๏ผไฝฟ็จไบ็กฌ็ผ็ fallback
// ๅจๅฎนๅจ/WSL2/ไบ็ฏๅขไธญ๏ผๆๆๅฎไพๅ
ฑไบซๅไธๅฏ้ฅ
usingFallbackKey = true
console.error('โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ')
console.error('โ โ ๏ธ ๅฎๅ
จ่ญฆๅ๏ผๆ ๆณ่ทๅๆบๅจๅฏไธๆ ่ฏ โ')
console.error('โ ๅฝๅ็ฏๅขๅฏ่ฝไธบๅฎนๅจ/WSL2/็ฒพ็ฎLinux๏ผๅ ๅฏๅฎๅ
จๆง้ไฝ โ')
console.error('โ ๅปบ่ฎฎ่ฟ่กไปฅไธๅฝไปค่ฎพ็ฝฎไธปๅฏ็ ๏ผ โ')
console.error('โ /masterkey <ไฝ ็ๅฏ็ > โ')
console.error('โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ')
// ไปไฝฟ็จๅบๅฎๅญ็ฌฆไธฒ๏ผไฝๆ ่ฎฐไธบไธๅฎๅ
จ
return createHash('sha256')
.update('cmdcode-v05-unsafe-fallback-key-do-not-use-in-production')
.digest()
}
/** ๅ ๅฏๆฐๆฎ */
export function encrypt(data: any): string {
const key = getEncryptionKey()
const iv = randomBytes(16)
const cipher = createCipheriv('aes-256-cbc', key, iv)
const json = JSON.stringify(data)
const encrypted = Buffer.concat([cipher.update(json, 'utf8'), cipher.final()])
return iv.toString('hex') + ':' + encrypted.toString('hex')
}
/** ่งฃๅฏๆฐๆฎ */
export function decrypt<T = any>(encryptedText: string): T {
const key = getEncryptionKey()
const [ivHex, dataHex] = encryptedText.split(':')
if (!ivHex || !dataHex) throw new Error('Invalid format')
const iv = Buffer.from(ivHex, 'hex')
const data = Buffer.from(dataHex, 'hex')
const decipher = createDecipheriv('aes-256-cbc', key, iv)
const json = decipher.update(data, undefined, 'utf8') + decipher.final('utf8')
return JSON.parse(json) as T
}
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
// ๅญๆฎต็บงๅ ๅฏ๏ผ็จไบๅฏ้ฅๆฑ ๏ผ
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
/** ๅ ๅฏๅไธชๅญๆฎต๏ผๅค็จ encrypt๏ผ็ปๆๅฝขๅฆ iv:data๏ผ */
export function encryptField(plaintext: string): string {
return encrypt(plaintext)
}
/** ่งฃๅฏๅไธชๅญๆฎต */
export function decryptField(encrypted: string): string {
return decrypt<string>(encrypted)
}
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
// ้
็ฝฎๆไปถ็ฎก็
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
function ensureDir(): void {
if (!existsSync(CMD_DIR)) mkdirSync(CMD_DIR, { recursive: true })
}
function setPermission(filePath: string): void {
try { chmodSync(filePath, 0o600) } catch { /* ignore */ }
}
/** ๅ ่ฝฝๆๆๆๆ้
็ฝฎ */
export function loadSecrets(): Secrets {
if (!existsSync(SECRETS_FILE)) return {}
try {
return decrypt<Secrets>(readFileSync(SECRETS_FILE, 'utf-8'))
} catch {
return {}
}
}
/** ไฟๅญๆๆๆๆ้
็ฝฎ */
export function saveSecrets(secrets: Secrets): void {
ensureDir()
writeFileSync(SECRETS_FILE, encrypt(secrets), 'utf-8')
setPermission(SECRETS_FILE)
}
/** ๆดๆฐ้จๅๆๆ้
็ฝฎ */
export function updateSecrets(partial: Partial<Secrets>): void {
const current = loadSecrets()
saveSecrets({ ...current, ...partial })
}
/** ๆธ
้คๆๆๆๆ้
็ฝฎ */
export function clearSecrets(): void {
if (existsSync(SECRETS_FILE)) {
try { unlinkSync(SECRETS_FILE) } catch { /* ignore */ }
}
}
export function hasSecrets(): boolean {
return existsSync(SECRETS_FILE)
}
export function maskSecret(value?: string): string {
if (!value) return '(ๆช้
็ฝฎ)'
return 'โ ๅทฒ้
็ฝฎ'
}
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
// ่ๅคฉๅฏ้ฅๆฑ ็ฎก็
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
/** ่ๅฐฝๆ ่ฎฐ๏ผๅบไบ name๏ผไธๅ็ดขๅผๅๅจๅฝฑๅ๏ผ */
const exhaustedChatKeyNames = new Set<string>()
let migrationWarned = false // P3 #35: ๅฏ้ฅไฝ็ณป่ฟ็งปๆ็คบ๏ผๅชๆพ็คบไธๆฌก๏ผ
const exhaustedEmbeddingKeyNames = new Set<string>()
/** ๅนถๅ้๏ผไฟๆคๅฏ้ฅๆฑ ๆไฝ๏ผ - ๅ็บฟ็จ็ฏๅขไธๅบๆฌๅฎๅ
จ๏ผไฝไธบๆชๆฅๅค็บฟ็จ้ข็ */
let keyPoolOperationInProgress = false
const pendingKeyPoolOperations: (() => void)[] = []
/** ๆง่กๅธฆ้็ๅฏ้ฅๆฑ ๆไฝ */
async function withKeyPoolLock<T>(fn: () => T): Promise<T> {
if (keyPoolOperationInProgress) {
return new Promise((resolve, reject) => {
pendingKeyPoolOperations.push(() => {
try {
resolve(fn())
} catch (e) {
reject(e)
}
})
})
}
keyPoolOperationInProgress = true
try {
return fn()
} finally {
keyPoolOperationInProgress = false
// ๆง่ก็ญๅพ
ไธญ็ๆไฝ
const next = pendingKeyPoolOperations.shift()
if (next) {
setTimeout(next, 0) // ๅผๆญฅๆง่ก๏ผ้ฟๅ
้ๅฝ
}
}
}
/** ๅ ่ฝฝ่ๅคฉๅฏ้ฅๆฑ */
export function loadChatKeyPool(): KeyPoolEntry[] {
const s = loadSecrets()
return s.chatKeyPool || []
}
/** ไฟๅญ่ๅคฉๅฏ้ฅๆฑ */
export function saveChatKeyPool(pool: KeyPoolEntry[]): void {
updateSecrets({ chatKeyPool: pool })
}
/** ๆทปๅ ่ๅคฉๅฏ้ฅ */
export function addChatKey(name: string, model: string, baseUrl: string, apiKey: string): void {
const pool = loadChatKeyPool()
if (pool.some(e => e.name === name)) {
throw new Error(`Key "${name}" already exists`)
}
pool.push({
name,
model,
baseUrl,
apiKeyEncrypted: encryptField(apiKey),
})
saveChatKeyPool(pool)
// ้็ฝฎ่ๅฐฝ็ถๆ๏ผ็กฎไฟๆฐ Key ๅฏ็จ
resetChatKeyPool()
}
/** ๅ ้ค่ๅคฉๅฏ้ฅ */
export function removeChatKey(name: string): boolean {
const pool = loadChatKeyPool()
const idx = pool.findIndex(e => e.name === name)
if (idx === -1) return false
pool.splice(idx, 1)
saveChatKeyPool(pool)
resetChatKeyPool()
return true
}
/** ้็ฝฎ่ๅคฉๅฏ้ฅๆฑ ่ๅฐฝ็ถๆ */
export function resetChatKeyPool(): void {
exhaustedChatKeyNames.clear()
}
/** ๆ ่ฎฐ่ๅคฉๅฏ้ฅไธบ่ๅฐฝ */
export function markChatKeyExhausted(name: string): void {
exhaustedChatKeyNames.add(name)
}
/** ่ทๅ่ๅคฉๅฏ้ฅๆฑ ็ถๆ */
export function getChatKeyPoolStatus(): { total: number; exhausted: number; remaining: number } {
const pool = loadChatKeyPool()
const total = pool.length
const exhausted = pool.filter(e => exhaustedChatKeyNames.has(e.name)).length
return { total, exhausted, remaining: total - exhausted }
}
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
// Embedding ๅฏ้ฅๆฑ ็ฎก็
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
export function loadEmbeddingKeyPool(): KeyPoolEntry[] {
const s = loadSecrets()
return s.embeddingKeyPool || []
}
export function saveEmbeddingKeyPool(pool: KeyPoolEntry[]): void {
updateSecrets({ embeddingKeyPool: pool })
}
export function addEmbeddingKey(name: string, model: string, baseUrl: string, apiKey: string): void {
const pool = loadEmbeddingKeyPool()
if (pool.some(e => e.name === name)) {
throw new Error(`Key "${name}" already exists`)
}
pool.push({
name,
model,
baseUrl,
apiKeyEncrypted: encryptField(apiKey),
})
saveEmbeddingKeyPool(pool)
resetEmbeddingKeyPool()
}
export function removeEmbeddingKey(name: string): boolean {
const pool = loadEmbeddingKeyPool()
const idx = pool.findIndex(e => e.name === name)
if (idx === -1) return false
pool.splice(idx, 1)
saveEmbeddingKeyPool(pool)
resetEmbeddingKeyPool()
return true
}
export function resetEmbeddingKeyPool(): void {
exhaustedEmbeddingKeyNames.clear()
}
export function markEmbeddingKeyExhausted(name: string): void {
exhaustedEmbeddingKeyNames.add(name)
}
export function getEmbeddingKeyPoolStatus(): { total: number; exhausted: number; remaining: number } {
const pool = loadEmbeddingKeyPool()
const total = pool.length
const exhausted = pool.filter(e => exhaustedEmbeddingKeyNames.has(e.name)).length
return { total, exhausted, remaining: total - exhausted }
}
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
// ๅฏ้ฅ่ทๅ๏ผ่งฃๅฏๅ่ฟๅๆๆ๏ผ็จๅฎ็ซๅณๆธ
็๏ผ
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
/** ๅ
้จ๏ผ่งฃๅฏๅฏ้ฅๆฑ ๆก็ฎ */
function decryptKeyPoolEntry(entry: KeyPoolEntry): { name: string; model: string; apiKey: string; baseUrl: string } | null {
try {
const apiKey = decryptField(entry.apiKeyEncrypted)
return { name: entry.name, model: entry.model, apiKey, baseUrl: entry.baseUrl }
} catch (e: any) {
// ่งฃๅฏๅคฑ่ดฅๆถ่ฎฐๅฝ่ญฆๅ๏ผไธๆด้ฒๆๆไฟกๆฏ๏ผ
console.warn(`โ ๏ธ ๅฏ้ฅ [${entry.name}] ่งฃๅฏๅคฑ่ดฅ๏ผๅฏ่ฝๅทฒๆๅๆไธปๅฏ้ฅไธๅน้
`)
console.warn(` ้่ฏฏ: ${e.message?.slice(0, 50) || 'unknown'}`)
console.warn(` ๅปบ่ฎฎ: ไฝฟ็จ /keypool remove ${entry.name} ๅ ้คๅ้ๆฐๆทปๅ `)
return null
}
}
/** ่ทๅ่ๅคฉ API Key - P3 #35: ๆฐๆงๅฏ้ฅไฝ็ณปๅนถๅญๆ็คบ */
export function getChatApiKey(): { name: string; model: string; apiKey: string; baseUrl: string } | null {
// 1. ็จๆท่ชๅฎไนๅฏ้ฅไผๅ
๏ผๅๅฏ้ฅๆจกๅผ๏ผ
const s = loadSecrets()
if (s.apiKey) {
// P3 #35: ๅฆๆๅๆถๆๅฏ้ฅๆฑ ๏ผๆ็คบ่ฟ็งป
if (s.chatKeyPool && s.chatKeyPool.length > 0 && !migrationWarned) {
migrationWarned = true
console.log(' ๐ก ๆ็คบ๏ผๅฝๅไฝฟ็จๅๅฏ้ฅๆจกๅผใ/keypool ๅฏๆทปๅ ๆดๅคๅฏ้ฅๅฎ็ฐ่ฝฎๆข')
}
return {
name: 'user-custom',
model: s.model || 'MiniMax-M2.7',
apiKey: s.apiKey,
baseUrl: s.baseUrl || 'https://api.minimaxi.com/v1'
}
}
// 2. ๅฏ้ฅๆฑ ๏ผๅคๅฏ้ฅๆจกๅผ๏ผ
const pool = loadChatKeyPool()
for (const entry of pool) {
if (!exhaustedChatKeyNames.has(entry.name)) {
const decrypted = decryptKeyPoolEntry(entry)
if (decrypted) {
return decrypted
}
}
}
return null
}
/** ๅๆข่ๅคฉๅฏ้ฅ๏ผ429 ๆถ่ฐ็จ๏ผ */
export function rotateChatApiKey(): { name: string; model: string; apiKey: string; baseUrl: string } | null {
const current = getChatApiKey()
if (!current) return null
// ๆ ่ฎฐๅฝๅไธบ่ๅฐฝ
markChatKeyExhausted(current.name)
console.log(` ๐ ๅฏ้ฅ [${current.name}] ๅทฒๆ ่ฎฐไธบ่ๅฐฝ`)
// ่ฟๅไธไธไธชๅฏ็จๅฏ้ฅ
return getChatApiKey()
}
/** ่ทๅ Embedding API Key */
export function getEmbeddingApiKey(): { name: string; apiKey: string } | null {
const pool = loadEmbeddingKeyPool()
for (const entry of pool) {
if (!exhaustedEmbeddingKeyNames.has(entry.name)) {
const decrypted = decryptKeyPoolEntry(entry)
if (decrypted) {
return { name: decrypted.name, apiKey: decrypted.apiKey }
}
}
}
return null
}
/** ๅๆข Embedding ๅฏ้ฅ */
export function rotateEmbeddingApiKey(): { name: string; apiKey: string } | null {
const current = getEmbeddingApiKey()
if (!current) return null
markEmbeddingKeyExhausted(current.name)
return getEmbeddingApiKey()
}
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
// ๅบ็จ้
็ฝฎ็ฎก็๏ผ้ๅฏ้ฅ๏ผ
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
export interface AppSettings {
permissions?: { allow?: string[]; defaultMode?: string }
modelType?: string
model?: string
language?: string
effortLevel?: string
skipDangerousModePermissionPrompt?: boolean
pluginWorkerForce?: string
systemPrompt?: string // ๅบๅฎๆ็คบ่ฏ๏ผๅ ๅจ็จๆท่พๅ
ฅๅ
[key: string]: any
}
export function saveAppConfig(config: AppSettings): void {
const s = loadSecrets()
saveSecrets({ ...s, ...config })
}
export function loadAppConfig(): AppSettings | null {
const s = loadSecrets()
const result: AppSettings = {}
if (s.permissions) result.permissions = s.permissions
if (s.modelType) result.modelType = s.modelType
if (s.model) result.model = s.model
if (s.language) result.language = s.language
if (s.effortLevel) result.effortLevel = s.effortLevel
if (s.skipDangerousModePermissionPrompt) result.skipDangerousModePermissionPrompt = s.skipDangerousModePermissionPrompt
if (s.pluginWorkerForce) result.pluginWorkerForce = s.pluginWorkerForce
if (s.systemPrompt) result.systemPrompt = s.systemPrompt
return Object.keys(result).length > 0 ? result : null
}
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
// ็จๆท็ผๅญ็ฎก็
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
export function saveUserCache(token: string, userInfo: { username: string; workspaceDir: string; quota?: number }): void {
updateSecrets({ userToken: token, userInfo })
}
export function loadUserCache(): { token: string; username: string; workspaceDir: string; quota?: number } | null {
const s = loadSecrets()
if (!s.userToken || !s.userInfo) return null
return { token: s.userToken, ...s.userInfo }
}
export function clearUserCache(): void {
const s = loadSecrets()
delete s.userToken
delete s.userInfo
saveSecrets(s)
}
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
// ๅ้่ฎฐๅฟ้
็ฝฎ
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
export const DEFAULT_MEMORY_SEARCH = {
provider: 'openai',
model: 'doubao-embedding-vision',
baseUrl: 'https://ark.cn-beijing.volces.com/api/coding/v3',
}
export function saveMemorySearchConfig(config: { baseUrl?: string; model?: string }): void {
const s = loadSecrets()
s.memorySearch = {
provider: 'openai',
model: config.model || DEFAULT_MEMORY_SEARCH.model,
baseUrl: config.baseUrl || DEFAULT_MEMORY_SEARCH.baseUrl,
}
saveSecrets(s)
}
export function loadMemorySearchConfig(): { provider: string; model: string; baseUrl: string } {
const s = loadSecrets()
if (s.memorySearch) {
return s.memorySearch
}
return DEFAULT_MEMORY_SEARCH
}
export function hasMemorySearchConfig(): boolean {
const pool = loadEmbeddingKeyPool()
return pool.length > 0
}
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
// ๅ
ผๅฎนๆง API
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
/** @deprecated ไฝฟ็จ getChatApiKey */
export const getCurrentMinimaxKey = getChatApiKey
/** @deprecated ไฝฟ็จ rotateChatApiKey */
export const rotateMinimaxKey = rotateChatApiKey
/** @deprecated ไฝฟ็จ getChatKeyPoolStatus */
export const getMinimaxKeyPoolStatus = getChatKeyPoolStatus
/** @deprecated ไฝฟ็จ resetChatKeyPool */
export const resetMinimaxKeyPool = resetChatKeyPool
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
// ๅฎๆถ้็ฝฎ๏ผๆฏๆฅๅๆจ้็ฝฎ่ๅฐฝ็ถๆ๏ผ
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
let resetTimer: ReturnType<typeof setInterval> | null = null
/** ๅฏๅจๆฏๆฅ่ชๅจ้็ฝฎ */
export function startDailyKeyPoolReset(): void {
if (resetTimer) return
// ่ฎก็ฎๅฐไธไธไธชๅๆจ็ๆถ้ด
const now = new Date()
const tomorrow = new Date(now)
tomorrow.setDate(tomorrow.getDate() + 1)
tomorrow.setHours(0, 0, 0, 0)
const msUntilMidnight = tomorrow.getTime() - now.getTime()
// ๅ
่ฎพ็ฝฎไธไธชๅฎๆถๅจๅฐๅๆจ
setTimeout(() => {
resetAllKeyPools()
// ็ถๅๆฏ24ๅฐๆถ้็ฝฎไธๆฌก
resetTimer = setInterval(resetAllKeyPools, 24 * 60 * 60 * 1000)
}, msUntilMidnight)
console.log(` โฐ ๅฏ้ฅๆฑ ๅฐๅจ ${Math.round(msUntilMidnight / 60000)} ๅ้ๅ่ชๅจ้็ฝฎ`)
}
/** ๅๆญข่ชๅจ้็ฝฎ */
export function stopDailyKeyPoolReset(): void {
if (resetTimer) {
clearInterval(resetTimer)
resetTimer = null
}
}
/** ้็ฝฎๆๆๅฏ้ฅๆฑ */
export function resetAllKeyPools(): void {
resetChatKeyPool()
resetEmbeddingKeyPool()
console.log(' โ
ๆๆๅฏ้ฅๆฑ ็ถๆๅทฒ้็ฝฎ๏ผๆฏๆฅ่ชๅจ๏ผ')
}