๐Ÿ“„ set.ts  โ€ข  7433 bytes
import { t } from '../i18n.js'
import { loadConfig, setApiKey, updateAppConfig } from '../config.js'
import { saveKeys, loadMemorySearchConfig, hasMemorySearchConfig, saveMemorySearchConfig } from '../apikeys.js'
import { testConnection } from '../models.js'
import { ChatEngine } from '../chat.js'

/**
 * ๅค„็† /set ็ณปๅˆ—ๅ‘ฝไปค๏ผš/set mem, /set interactive, /set <key>
 * 
 * ่ฟ”ๅ›ž { handled, config, engine } โ€” ๅฆ‚ๆžœ handled=true๏ผŒcli.ts ๅบ” continue
 * config ๅ’Œ engine ๅฏ่ƒฝ่ขซไฟฎๆ”น๏ผˆAPI้…็ฝฎๅ˜ๆ›ดๆ—ถ้œ€่ฆๅŒๆญฅๅ›žไธปๅพช็Žฏ๏ผ‰
 */
export async function handleSetCommand(
  trimmed: string,
  config: any,
  engine: any,
  color: any,
  ACCENT: string,
  MUTED: string,
  SUCCESS: string,
  ERROR: string,
  WARN: string,
  askQuestion: (q: string) => Promise<string>
): Promise<{ handled: boolean; config: any; engine: any }> {

  // /set mem / /set memory โ€” ๅ‘้‡่ฎฐๅฟ† API ๅฏ†้’ฅ้…็ฝฎ
  if (trimmed === '/set mem' || trimmed === '/set memory') {
    console.log('')
    console.log(`  ${ACCENT}๐Ÿง  ${t('memory.api_config')}${color.reset}`)
    console.log(`  ${ACCENT}โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€${color.reset}`)
    
    // ๆ˜พ็คบๅฝ“ๅ‰็Šถๆ€
    const currentConfig = loadMemorySearchConfig()
    const isCustom = hasMemorySearchConfig()
    console.log(`  ${MUTED}${t('memory.current_status')} ${isCustom ? t('memory.custom_key') : t('memory.using_default')}${color.reset}`)
    console.log(`  ${MUTED}${t('memory.api_url')}${currentConfig.baseUrl}${color.reset}`)
    console.log(`  ${MUTED}${t('memory.model_name')}${currentConfig.model}${color.reset}`)
    console.log('')
    
    const newKey = await askQuestion(`  ${ACCENT}${t("memory.new_key_prompt")}${color.reset}`)
    if (!newKey.trim() || newKey.trim().toLowerCase() === 'q') {
      console.log(`  ${MUTED}${t('memory.keep_current')}${color.reset}`)
      console.log('')
      return { handled: true, config, engine }
    }
    
    // ไฟๅญ˜ๆ–ฐๅฏ†้’ฅ
    saveMemorySearchConfig({ apiKey: newKey.trim() })
    console.log('')
    console.log(`  ${SUCCESS}โœ… ${t('memory.api_key_encrypted')}${color.reset}`)
    console.log('')
    return { handled: true, config, engine }
  }

  // /set / /set interactive โ€” ไบคไบ’ๅผ่ฎพ็ฝฎ API ้…็ฝฎ
  if (trimmed === '/set' || trimmed === '/set interactive') {
    console.log('')
    console.log(`  ${ACCENT}๐Ÿ”ง ${t('api.wizard_title')}${color.reset}`)
    console.log(`  ${ACCENT}โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€${color.reset}`)
    console.log(`  ${MUTED}${t('api.wizard_hint')}${color.reset}`)
    console.log(`  ${MUTED}${t("api.op_hint")}${color.reset}`)
    console.log('')

    // 1. URL ๅœฐๅ€
    let baseUrl = ''
    while (!baseUrl.trim()) {
      baseUrl = await askQuestion(`  ${ACCENT}${t("api.url_prompt")}${color.reset}`)
      if (baseUrl.trim().toLowerCase() === 'q') {
        console.log(`  ${MUTED}${t('model.op_cancel')}${color.reset}`)
        break
      }
    }
    if (!baseUrl.trim()) return { handled: true, config, engine }

    // 2. API Key
    let apiKey = ''
    while (!apiKey.trim()) {
      apiKey = await askQuestion(`  ${ACCENT}${t("api.key_prompt")}${color.reset}`)
      if (apiKey.trim().toLowerCase() === 'q') {
        console.log(`  ${MUTED}${t('model.op_cancel')}${color.reset}`)
        break
      }
    }
    if (!apiKey.trim()) return { handled: true, config, engine }

    // 3. ๆจกๅž‹ๅž‹ๅท
    let model = ''
    while (!model.trim()) {
      model = await askQuestion(`  ${ACCENT}${t('model.custom_title')} (Q ๅ–ๆถˆ): ${color.reset}`)
      if (model.trim().toLowerCase() === 'q') {
        console.log(`  ${MUTED}${t('model.op_cancel')}${color.reset}`)
        break
      }
    }
    if (!model.trim()) return { handled: true, config, engine }

    // 4. ๆจกๅž‹ๆ˜พ็คบๅ็งฐ
    const displayName = await askQuestion(`  ${ACCENT}ๆจกๅž‹ๆ˜พ็คบๅ็งฐ (ๅ›ž่ฝฆ่ทณ่ฟ‡): ${color.reset}`) || model

    // ไฟๅญ˜้…็ฝฎ๏ผˆๅŠ ๅฏ†๏ผ‰
    saveKeys(apiKey, baseUrl)
    updateAppConfig({ model, displayName: displayName || model })

    // ๆต‹่ฏ•่”้€šๆ€ง
    console.log('')
    console.log(`  ${ACCENT}โณ ${t("api.testing")}${color.reset}`)

    const testConfig = {
      apiKey: apiKey,
      baseUrl: baseUrl,
      model: model,
      timeoutMs: 30000,
    }

    try {
      const testEngine = new ChatEngine(testConfig)
      await testEngine.chat('Hi')

      // ่ฟžๆŽฅๆˆๅŠŸ๏ผŒๅบ”็”จ้…็ฝฎ
      config.apiKey = apiKey
      config.baseUrl = baseUrl
      config.model = model
      Object.assign(engine, new ChatEngine(config))

      console.log('')
      console.log(`  ${SUCCESS}โœ… ${t('api.config_done')}${color.reset}`)
      console.log(`  ${MUTED}  URL: ${baseUrl}${color.reset}`)
      console.log(`  ${MUTED}  API Key: โœ“ ${t('api.key_encrypted')}${color.reset}`)
      console.log(`  ${MUTED}  ${t("status.model")}: ${model}${color.reset}`)
      console.log(`  ${MUTED}  ${t('api.display_name')}${displayName || model}${color.reset}`)
      console.log('')
    } catch (e: any) {
      // ่ฟžๆŽฅๅคฑ่ดฅ๏ผŒๆขๅค้ป˜่ฎค้…็ฝฎ
      console.log(`  ${ERROR}โŒ ${t('api.connect_failed')} ${e.message}${color.reset}`)
      console.log(`  ${WARN}${t('api.fallback_default')}${color.reset}`)

      const defaultConfig = loadConfig()
      config.apiKey = defaultConfig.apiKey
      config.baseUrl = defaultConfig.baseUrl
      config.model = defaultConfig.model
      Object.assign(engine, new ChatEngine(config))

      console.log(`  ${MUTED}${t('api.switched_default')}${config.model}${color.reset}`)
      console.log('')
      console.log(`  ${WARN}${t("error.model_saved_not_applied")}${color.reset}`)
      console.log('')
    }
    return { handled: true, config, engine }
  }

  // /set <key> โ€” ๅฟซๆท่ฎพ็ฝฎ API Key
  if (trimmed.startsWith('/set ')) {
    const parts = trimmed.slice(5).trim().split(/\s+/)
    if (!parts[0] || parts[0] === '') {
      console.log(`  ${ACCENT}${t('api.usage')}${color.reset}`)
      console.log(`    ${ACCENT}/set${color.reset}              ${MUTED}${t('api.set_interactive')}${color.reset}`)
      console.log(`    ${ACCENT}/set <api_key>${color.reset}    ${MUTED}${t('api.set_quick')}${color.reset}`)
      return { handled: true, config, engine }
    }
    const apiKey = parts[0]
    const baseUrl = parts[1] || 'https://api.minimax.chat/v1'
    
    // ๅฟซๆท่ฎพ็ฝฎไนŸ้œ€่ฆ่ฟžๆŽฅๆต‹่ฏ•
    process.stdout.write(`  ${ACCENT}${t("api.testing")}${color.reset}`)
    const testResult = await testConnection(baseUrl, apiKey, config.model)
    process.stdout.write('\r' + ' '.repeat(40) + '\r')
    
    if (!testResult.success) {
      console.log(`  ${ERROR}${t('model.test_failed')} ${testResult.error}${color.reset}`)
      console.log(`  ${MUTED}${t('api.key_not_saved')}${color.reset}`)
      return { handled: true, config, engine }
    }
    
    setApiKey(apiKey, baseUrl)
    console.log(`  ${SUCCESS}${t('api.key_stored')}${color.reset}`)
    config.apiKey = apiKey
    config.baseUrl = baseUrl
    Object.assign(engine, new ChatEngine(config))
    console.log(`  ${SUCCESS}${t('api.engine_reinit')} (${testResult.latencyMs}ms)${color.reset}`)
    return { handled: true, config, engine }
  }

  return { handled: false, config, engine }
}