{"version":3,"file":"api-command.mjs","names":[],"sources":["../../../src/commands/api-command.ts"],"sourcesContent":["/**\n * API key management command.\n *\n * /api              — List configured providers + active provider\n * /api set <p> <k>  — Store an API key for a provider in macOS Keychain\n * /api rm <p>       — Remove a stored key\n * /api use <p>      — Switch the active LLM provider\n */\n\nimport {\n  PROVIDERS, storeApiKey, removeApiKey, listStoredProviders,\n  getActiveProvider, setActiveProvider, maskKey, loadApiKey,\n} from '../services/keychain-secrets.js';\n\nexport const apiCommand = {\n  name: 'api',\n  description: 'Manage LLM API keys: /api, /api set <provider> <key>, /api rm <provider>, /api use <provider>',\n  acceptsArgs: true,\n  requireAuth: true,\n  handler: async (ctx?: any) => {\n    const rawArgs = (ctx?.args ?? '').trim();\n    const parts = rawArgs.split(/\\s+/);\n    const subcommand = parts[0] || 'list';\n\n    switch (subcommand) {\n      case 'list':\n        return handleList();\n      case 'set':\n        return handleSet(parts[1], parts.slice(2).join(' '));\n      case 'rm':\n      case 'remove':\n        return handleRemove(parts[1]);\n      case 'use':\n      case 'switch':\n        return handleUse(parts[1]);\n      case 'providers':\n        return handleProviders();\n      default:\n        return {\n          text: `Unknown subcommand: \"${subcommand}\".\\n\\n` +\n            'Usage:\\n' +\n            '  `/api` — list configured keys\\n' +\n            '  `/api set <provider> <key>` — store a key\\n' +\n            '  `/api rm <provider>` — remove a key\\n' +\n            '  `/api use <provider>` — switch active provider\\n' +\n            '  `/api providers` — list all known providers',\n        };\n    }\n  },\n};\n\n// ─── Handlers ────────────────────────────────────────────────────────────\n\nfunction handleList() {\n  const active = getActiveProvider();\n  const stored = listStoredProviders();\n  const lines: string[] = ['**LLM API Keys**', ''];\n\n  for (const [name, config] of Object.entries(PROVIDERS)) {\n    const isStored = stored.includes(name);\n    const fromEnv = !!process.env[config.envVar];\n    const isActive = name === active;\n\n    let status = 'not configured';\n    if (isStored && fromEnv) {\n      // Key from keychain loaded into env\n      const key = loadApiKey(name);\n      status = `configured (Keychain) ${maskKey(key ?? '')}`;\n    } else if (fromEnv) {\n      status = `configured (env var)`;\n    } else if (isStored) {\n      const key = loadApiKey(name);\n      status = `stored (Keychain) ${maskKey(key ?? '')} — not loaded`;\n    }\n\n    const marker = isActive ? ' **← active**' : '';\n    lines.push(`  **${config.label}** (\\`${name}\\`): ${status}${marker}`);\n  }\n\n  lines.push('');\n  lines.push(`Active provider: **${active}**`);\n  lines.push('');\n  lines.push('`/api set <provider> <key>` to add. `/api use <provider>` to switch.');\n\n  if (process.platform === 'darwin') {\n    lines.push('Keys stored in macOS Keychain (encrypted, persists across sessions).');\n  } else {\n    lines.push('Keys stored in `~/.openclawnch/api-keys.json` (file permissions 0600).');\n  }\n\n  return { text: lines.join('\\n') };\n}\n\nfunction handleSet(provider: string | undefined, key: string | undefined) {\n  if (!provider || !key) {\n    return { text: 'Usage: `/api set <provider> <key>`\\n\\nProviders: ' + Object.keys(PROVIDERS).join(', ') };\n  }\n\n  const normalized = provider.toLowerCase();\n  if (!PROVIDERS[normalized]) {\n    return {\n      text: `Unknown provider \"${provider}\". Known providers: ${Object.keys(PROVIDERS).join(', ')}`,\n    };\n  }\n\n  // Basic key format validation\n  const config = PROVIDERS[normalized]!;\n  if (key.length < 10) {\n    return { text: 'API key seems too short. Double-check and try again.' };\n  }\n\n  if (config.prefix && !key.startsWith(config.prefix)) {\n    // Warn but don't block — some keys may have non-standard prefixes\n    const warning = `\\n\\nNote: Expected ${config.label} keys to start with \\`${config.prefix}\\`. Your key doesn't match — double-check it's correct.`;\n    try {\n      storeApiKey(normalized, key);\n    } catch (err) {\n      return { text: `Failed to store key: ${err instanceof Error ? err.message : String(err)}` };\n    }\n    return {\n      text: `API key for **${config.label}** stored and loaded into this session.${warning}\\n\\nUse \\`/api use ${normalized}\\` to switch to this provider.`,\n    };\n  }\n\n  try {\n    storeApiKey(normalized, key);\n  } catch (err) {\n    return { text: `Failed to store key: ${err instanceof Error ? err.message : String(err)}` };\n  }\n\n  return {\n    text: `API key for **${config.label}** stored and loaded into this session.\\n\\nUse \\`/api use ${normalized}\\` to switch to this provider.`,\n  };\n}\n\nfunction handleRemove(provider: string | undefined) {\n  if (!provider) {\n    return { text: 'Usage: `/api rm <provider>`\\n\\nProviders: ' + Object.keys(PROVIDERS).join(', ') };\n  }\n\n  const normalized = provider.toLowerCase();\n  const config = PROVIDERS[normalized];\n  if (!config) {\n    return { text: `Unknown provider \"${provider}\".` };\n  }\n\n  const removed = removeApiKey(normalized);\n  if (removed) {\n    return { text: `API key for **${config.label}** removed from storage and unloaded from this session.` };\n  }\n  return { text: `No stored key found for **${config.label}**.` };\n}\n\nfunction handleUse(provider: string | undefined) {\n  if (!provider) {\n    return { text: 'Usage: `/api use <provider>`\\n\\nProviders: ' + Object.keys(PROVIDERS).join(', ') };\n  }\n\n  const normalized = provider.toLowerCase();\n  const config = PROVIDERS[normalized];\n  if (!config) {\n    return { text: `Unknown provider \"${provider}\". Known: ${Object.keys(PROVIDERS).join(', ')}` };\n  }\n\n  // Check if key is available\n  const hasKey = !!process.env[config.envVar];\n  if (!hasKey) {\n    return {\n      text: `No API key configured for **${config.label}**. Set one first:\\n\\`/api set ${normalized} <your-key>\\``,\n    };\n  }\n\n  const prev = getActiveProvider();\n  setActiveProvider(normalized);\n  return {\n    text: `Switched LLM provider: **${PROVIDERS[prev]?.label ?? prev}** → **${config.label}**\\n\\nNew requests will use ${config.label}. Model selection may need updating (\\`/llm\\`).`,\n  };\n}\n\nfunction handleProviders() {\n  const lines = ['**Known LLM Providers**', ''];\n  for (const [name, config] of Object.entries(PROVIDERS)) {\n    lines.push(`  **${name}** — ${config.label} (env: \\`${config.envVar}\\`)`);\n  }\n  lines.push('', 'Use `/api set <provider> <key>` to configure.');\n  return { text: lines.join('\\n') };\n}\n"],"mappings":"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"}