{"repo": "agenticmatrix/free-claude-code", "files": {"config/default_settings.json": {"a06444d6de7882ebc0c76f271e35003582dd6351": {"t": 1785674155.3692315, "n": 3810, "h": "84524127f36e5893", "c": "{\n \"theme\": \"light\",\n \"max_tokens\": 32768,\n \"default_model\": \"deepseek/deepseek-v4-pro\",\n \"model_list\": [\n {\n \"model\": [\n {\n \"name\": \"deepseek-v4-pro\",\n \"price\": {\n \"input\": 3,\n \"cache_read_input\": 0.025,\n \"output\": 6,\n \"currency\": \"CNY\",\n \"unit\": 1000000,\n \"concurrency\": 500,\n \"max_context\": 1000000\n }\n },\n {\n \"name\": \"deepseek-v4-flash\",\n \"price\": {\n \"input\": 1,\n \"cache_read_input\": 0.02,\n \"output\": 2,\n \"currency\": \"CNY\",\n \"unit\": 1000000,\n \"concurrency\": 2500,\n \"max_context\": 1000000\n }\n },\n \"deepseek-chat\",\n \"deepseek-reasoner\"\n ],\n \"base_url\": \"https://api.deepseek.com/anthropic\",\n \"auth_token_env\": \"YOUR_DEEPSEEK_API_KEY\",\n \"provider\": \"deepseek\"\n },\n {\n \"model\": [\n \"claude-sonnet-2025\",\n \"opus-4.8\"\n ],\n \"base_url\": \"https://api.deepseek.com/anthropic\",\n \"auth_token_env\": \"YOUR_ANTHROPIC_API_KEY\",\n \"provider\": \"anthropic\",\n \"price\": {\n \"input\": 3,\n \"output\": 15,\n \"currency\": \"USD\",\n \"unit\": \"1M tokens\"\n }\n },\n {\n \"model\": [\n \"glm-4-plus\",\n \"glm-4-flash\",\n \"glm-4-air\"\n ],\n \"base_url\": \"https://open.bigmodel.cn/api/paas/v4\",\n \"auth_token_env\": \"YOUR_ZHIPU_API_KEY\",\n \"provider\": \"glm\",\n \"price\": {\n \"input\": 0.1,\n \"output\": 0.1,\n \"currency\": \"CNY\",\n \"unit\": \"1K tokens\"\n }\n },\n {\n \"model\": [\n \"MiniMax-Text-01\",\n \"abab7-chat\",\n \"abab6.5s-chat\"\n ],\n \"base_url\": \"https://api.minimax.chat/v1\",\n \"auth_token_env\": \"YOUR_MINIMAX_API_KEY\",\n \"provider\": \"minimax\",\n \"price\": {\n \"input\": 10,\n \"output\": 10,\n \"currency\": \"CNY\",\n \"unit\": \"1M tokens\"\n }\n },\n {\n \"model\": [\n \"local-llama3-70b\",\n \"local-qwen-32b\",\n \"local-mistral\"\n ],\n \"base_url\": \"http://localhost:8000/v1\",\n \"auth_token_env\": \"LOCAL_NO_KEY\",\n \"provider\": \"local\",\n \"price\": {\n \"input\": 0,\n \"output\": 0,\n \"currency\": \"USD\",\n \"unit\": \"free\"\n }\n },\n {\n \"model\": [\n \"qwen-max\",\n \"qwen-plus\",\n \"qwen-turbo\"\n ],\n \"base_url\": \"https://dashscope.aliyuncs.com/compatible-mode/v1\",\n \"auth_token_env\": \"YOUR_QWEN_API_KEY\",\n \"provider\": \"qwen\",\n \"price\": {\n \"input\": 0.02,\n \"output\": 0.06,\n \"currency\": \"CNY\",\n \"unit\": \"1K tokens\"\n }\n },\n {\n \"model\": [\n \"gpt-4-turbo\",\n \"gpt-4o\",\n \"gpt-3.5-turbo\"\n ],\n \"base_url\": \"https://api.openai.com/v1\",\n \"auth_token_env\": \"YOUR_OPENAI_API_KEY\",\n \"provider\": \"openai\",\n \"price\": {\n \"input\": 10,\n \"output\": 30,\n \"currency\": \"USD\",\n \"unit\": \"1M tokens\"\n }\n },\n {\n \"model\": [\n \"grok-1\",\n \"grok-beta\",\n \"grok-vision-beta\"\n ],\n \"base_url\": \"https://api.x.ai/v1\",\n \"auth_token_env\": \"YOUR_GROK_API_KEY\",\n \"provider\": \"grok\",\n \"price\": {\n \"input\": 5,\n \"output\": 15,\n \"currency\": \"USD\",\n \"unit\": \"1M tokens\"\n }\n },\n {\n \"model\": [\n \"gemini-1.5-pro\",\n \"gemini-1.5-flash\",\n \"gemini-1.0-pro\"\n ],\n \"base_url\": \"https://generativelanguage.googleapis.com/v1\",\n \"auth_token_env\": \"YOUR_GOOGLE_API_KEY\",\n \"provider\": \"google\",\n \"price\": {\n \"input\": 3.5,\n \"output\": 10.5,\n \"currency\": \"USD\",\n \"unit\": \"1M tokens\"\n }\n }\n ]\n}\n"}}, "src/cli/config.ts": {"a06444d6de7882ebc0c76f271e35003582dd6351": {"t": 1785746050.035061, "n": 6986, "h": "77c06f80fba2b92d", "c": "import { existsSync, mkdirSync, readFileSync, copyFileSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { join, dirname } from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\nimport type { AppConfig } from '../types.js';\n\n// ---------------------------------------------------------------------------\n// Settings types — matches ~/.coder/settings.json format\n// ---------------------------------------------------------------------------\n\nexport interface ModelPrice {\n input: number;\n output: number;\n cache_read_input?: number;\n currency?: string;\n unit?: number;\n concurrency?: number;\n max_context?: number;\n}\n\nexport interface ModelItem {\n name: string;\n price?: ModelPrice;\n}\n\nexport interface ModelEntry {\n /** List of model IDs available for this provider — strings or {name, price} objects */\n model: Array;\n /** Provider endpoint URL */\n base_url?: string;\n /** API key / auth token */\n auth_token_env?: string;\n /** HTTP/HTTPS proxy URL */\n proxy?: string;\n /** Maximum output tokens for this provider */\n max_tokens?: number;\n /** Provider identifier (anthropic, deepseek, openai, etc.) */\n provider?: string;\n}\n\nexport interface CoderSettings {\n env?: Record;\n model_list?: ModelEntry[];\n /** Format: \"provider/model-name\" (e.g. \"deepseek/deepseek-v4-pro\") */\n default_model?: string;\n /** Global max output tokens (default: 32768) */\n max_tokens?: number;\n /** UI theme (dark / light) */\n theme?: string;\n /** Max concurrent tool executions (default: 32, range: 1-256). */\n max_tool_concurrency?: number;\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nexport function inferProvider(model: string): string {\n const lower = model.toLowerCase();\n if (lower.includes('deepseek')) return 'deepseek';\n if (lower.includes('openai') || lower.includes('gpt') || lower.includes('o1') || lower.includes('o3') || lower.includes('o4')) return 'openai';\n return 'anthropic';\n}\n\nexport function loadSettings(): CoderSettings {\n const settingsDir = join(homedir(), '.coder');\n const settingsPath = join(settingsDir, 'settings.json');\n\n if (!existsSync(settingsPath)) {\n // Copy default settings on first install\n const defaultSettingsPath = join(\n dirname(fileURLToPath(import.meta.url)),\n '..', '..', 'config', 'default_settings.json',\n );\n if (existsSync(defaultSettingsPath)) {\n mkdirSync(settingsDir, { recursive: true });\n copyFileSync(defaultSettingsPath, settingsPath);\n }\n }\n\n try {\n const raw = readFileSync(settingsPath, 'utf-8');\n return JSON.parse(raw) as CoderSettings;\n } catch {\n return {};\n }\n}\n\n/** Resolve max tool concurrency from settings, with bounds checking. */\nexport function getMaxToolConcurrency(settings?: CoderSettings): number {\n const val = settings?.max_tool_concurrency;\n if (typeof val === 'number' && val >= 1 && val <= 256) return val;\n return 32;\n}\n\n// ---------------------------------------------------------------------------\n// Model resolution — matches KodeAgent priority:\n// 1. CODER_MODEL env var (highest)\n// 2. default_model parsed as \"provider/model-name\", looked up in model_list\n// 3. First entry in model_list\n// 4. Legacy env vars in settings.env\n// ---------------------------------------------------------------------------\n\nfunction resolveModel(settings: CoderSettings): {\n model: string;\n baseUrl: string;\n apiKey: string;\n proxy?: string;\n maxTokens?: number;\n provider: string;\n currency?: string;\n inputPrice?: number;\n outputPrice?: number;\n cacheReadPrice?: number;\n maxContext?: number;\n} {\n const parseDefault = (raw: string) => {\n const parts = raw.split('/');\n return { providerName: parts[0]!, modelName: parts.length > 1 ? parts[1] : undefined };\n };\n\n const modelName = (m: string | ModelItem): string =>\n typeof m === 'string' ? m : m.name;\n\n const modelPrice = (m: string | ModelItem): ModelPrice | undefined =>\n typeof m === 'string' ? undefined : m.price;\n\n const resolveFromEntry = (entry: ModelEntry, preferredModel?: string) => {\n const list = entry.model;\n const found = preferredModel\n ? list.find(m => modelName(m) === preferredModel) ?? list[0]\n : list[0];\n const selectedModel = modelName(found!);\n const price = found ? modelPrice(found) : undefined;\n return {\n model: selectedModel,\n baseUrl: entry.base_url ?? '',\n apiKey: entry.auth_token_env ?? '',\n proxy: entry.proxy,\n maxTokens: entry.max_tokens,\n provider: entry.provider ?? inferProvider(selectedModel),\n currency: price?.currency,\n inputPrice: price?.input,\n outputPrice: price?.output,\n cacheReadPrice: price?.cache_read_input,\n maxContext: price?.max_context,\n };\n };\n\n // 1. default_model from settings (\"provider/model-name\" format)\n const defaultName = settings.default_model;\n if (defaultName && settings.model_list) {\n const { providerName, modelName } = parseDefault(defaultName);\n const entry = settings.model_list.find(m => m.provider === providerName);\n if (entry && entry.model.length > 0) {\n return resolveFromEntry(entry, modelName);\n }\n }\n\n // 2. First entry in model_list\n if (settings.model_list && settings.model_list.length > 0) {\n const entry = settings.model_list[0]!;\n if (entry.model.length > 0) {\n return resolveFromEntry(entry);\n }\n }\n\n throw new Error(\n 'No model configured. Add model_list to ~/.coder/settings.json.',\n );\n}\n\n// ---------------------------------------------------------------------------\n// Public API — loadConfig\n// ---------------------------------------------------------------------------\n\n/**\n * Load AI model configuration from ~/.coder/settings.json.\n *\n * Resolution priority:\n * 1. default_model — \"provider/model-name\" format, looked up in model_list\n * 2. First entry in model_list\n *\n * Settings format:\n * {\n * \"model_list\": [\n * { \"model\": [\"deepseek-v4-pro\"], \"provider\": \"deepseek\",\n * \"base_url\": \"https://api.deepseek.com/anthropic\", \"auth_token_env\": \"sk-...\" }\n * ],\n * \"default_model\": \"deepseek/deepseek-v4-pro\"\n * }\n */\nexport function loadConfig(): AppConfig {\n const settings = loadSettings();\n\n const resolved = resolveModel(settings);\n\n const model = resolved.model;\n const apiKey = resolved.apiKey;\n const baseUrl = resolved.baseUrl;\n const proxy = resolved.proxy;\n const maxTokens = resolved.maxTokens ?? settings.max_tokens;\n\n if (!model) {\n throw new Error(\n 'No model configured. Set default_model in ~/.coder/settings.json.',\n );\n }\n\n return { baseUrl, apiKey, model, provider: resolved.provider, proxy, maxTokens, currency: resolved.currency, inputPrice: resolved.inputPrice, outputPrice: resolved.outputPrice, cacheReadPrice: resolved.cacheReadPrice, maxContext: resolved.maxContext };\n}\n"}, "f9bebc4ab96fc1f3b8077486e24baf4de3384693": {"t": 1785759214.11404, "n": 6986, "h": "77c06f80fba2b92d", "c": "import { existsSync, mkdirSync, readFileSync, copyFileSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { join, dirname } from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\nimport type { AppConfig } from '../types.js';\n\n// ---------------------------------------------------------------------------\n// Settings types — matches ~/.coder/settings.json format\n// ---------------------------------------------------------------------------\n\nexport interface ModelPrice {\n input: number;\n output: number;\n cache_read_input?: number;\n currency?: string;\n unit?: number;\n concurrency?: number;\n max_context?: number;\n}\n\nexport interface ModelItem {\n name: string;\n price?: ModelPrice;\n}\n\nexport interface ModelEntry {\n /** List of model IDs available for this provider — strings or {name, price} objects */\n model: Array;\n /** Provider endpoint URL */\n base_url?: string;\n /** API key / auth token */\n auth_token_env?: string;\n /** HTTP/HTTPS proxy URL */\n proxy?: string;\n /** Maximum output tokens for this provider */\n max_tokens?: number;\n /** Provider identifier (anthropic, deepseek, openai, etc.) */\n provider?: string;\n}\n\nexport interface CoderSettings {\n env?: Record;\n model_list?: ModelEntry[];\n /** Format: \"provider/model-name\" (e.g. \"deepseek/deepseek-v4-pro\") */\n default_model?: string;\n /** Global max output tokens (default: 32768) */\n max_tokens?: number;\n /** UI theme (dark / light) */\n theme?: string;\n /** Max concurrent tool executions (default: 32, range: 1-256). */\n max_tool_concurrency?: number;\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nexport function inferProvider(model: string): string {\n const lower = model.toLowerCase();\n if (lower.includes('deepseek')) return 'deepseek';\n if (lower.includes('openai') || lower.includes('gpt') || lower.includes('o1') || lower.includes('o3') || lower.includes('o4')) return 'openai';\n return 'anthropic';\n}\n\nexport function loadSettings(): CoderSettings {\n const settingsDir = join(homedir(), '.coder');\n const settingsPath = join(settingsDir, 'settings.json');\n\n if (!existsSync(settingsPath)) {\n // Copy default settings on first install\n const defaultSettingsPath = join(\n dirname(fileURLToPath(import.meta.url)),\n '..', '..', 'config', 'default_settings.json',\n );\n if (existsSync(defaultSettingsPath)) {\n mkdirSync(settingsDir, { recursive: true });\n copyFileSync(defaultSettingsPath, settingsPath);\n }\n }\n\n try {\n const raw = readFileSync(settingsPath, 'utf-8');\n return JSON.parse(raw) as CoderSettings;\n } catch {\n return {};\n }\n}\n\n/** Resolve max tool concurrency from settings, with bounds checking. */\nexport function getMaxToolConcurrency(settings?: CoderSettings): number {\n const val = settings?.max_tool_concurrency;\n if (typeof val === 'number' && val >= 1 && val <= 256) return val;\n return 32;\n}\n\n// ---------------------------------------------------------------------------\n// Model resolution — matches KodeAgent priority:\n// 1. CODER_MODEL env var (highest)\n// 2. default_model parsed as \"provider/model-name\", looked up in model_list\n// 3. First entry in model_list\n// 4. Legacy env vars in settings.env\n// ---------------------------------------------------------------------------\n\nfunction resolveModel(settings: CoderSettings): {\n model: string;\n baseUrl: string;\n apiKey: string;\n proxy?: string;\n maxTokens?: number;\n provider: string;\n currency?: string;\n inputPrice?: number;\n outputPrice?: number;\n cacheReadPrice?: number;\n maxContext?: number;\n} {\n const parseDefault = (raw: string) => {\n const parts = raw.split('/');\n return { providerName: parts[0]!, modelName: parts.length > 1 ? parts[1] : undefined };\n };\n\n const modelName = (m: string | ModelItem): string =>\n typeof m === 'string' ? m : m.name;\n\n const modelPrice = (m: string | ModelItem): ModelPrice | undefined =>\n typeof m === 'string' ? undefined : m.price;\n\n const resolveFromEntry = (entry: ModelEntry, preferredModel?: string) => {\n const list = entry.model;\n const found = preferredModel\n ? list.find(m => modelName(m) === preferredModel) ?? list[0]\n : list[0];\n const selectedModel = modelName(found!);\n const price = found ? modelPrice(found) : undefined;\n return {\n model: selectedModel,\n baseUrl: entry.base_url ?? '',\n apiKey: entry.auth_token_env ?? '',\n proxy: entry.proxy,\n maxTokens: entry.max_tokens,\n provider: entry.provider ?? inferProvider(selectedModel),\n currency: price?.currency,\n inputPrice: price?.input,\n outputPrice: price?.output,\n cacheReadPrice: price?.cache_read_input,\n maxContext: price?.max_context,\n };\n };\n\n // 1. default_model from settings (\"provider/model-name\" format)\n const defaultName = settings.default_model;\n if (defaultName && settings.model_list) {\n const { providerName, modelName } = parseDefault(defaultName);\n const entry = settings.model_list.find(m => m.provider === providerName);\n if (entry && entry.model.length > 0) {\n return resolveFromEntry(entry, modelName);\n }\n }\n\n // 2. First entry in model_list\n if (settings.model_list && settings.model_list.length > 0) {\n const entry = settings.model_list[0]!;\n if (entry.model.length > 0) {\n return resolveFromEntry(entry);\n }\n }\n\n throw new Error(\n 'No model configured. Add model_list to ~/.coder/settings.json.',\n );\n}\n\n// ---------------------------------------------------------------------------\n// Public API — loadConfig\n// ---------------------------------------------------------------------------\n\n/**\n * Load AI model configuration from ~/.coder/settings.json.\n *\n * Resolution priority:\n * 1. default_model — \"provider/model-name\" format, looked up in model_list\n * 2. First entry in model_list\n *\n * Settings format:\n * {\n * \"model_list\": [\n * { \"model\": [\"deepseek-v4-pro\"], \"provider\": \"deepseek\",\n * \"base_url\": \"https://api.deepseek.com/anthropic\", \"auth_token_env\": \"sk-...\" }\n * ],\n * \"default_model\": \"deepseek/deepseek-v4-pro\"\n * }\n */\nexport function loadConfig(): AppConfig {\n const settings = loadSettings();\n\n const resolved = resolveModel(settings);\n\n const model = resolved.model;\n const apiKey = resolved.apiKey;\n const baseUrl = resolved.baseUrl;\n const proxy = resolved.proxy;\n const maxTokens = resolved.maxTokens ?? settings.max_tokens;\n\n if (!model) {\n throw new Error(\n 'No model configured. Set default_model in ~/.coder/settings.json.',\n );\n }\n\n return { baseUrl, apiKey, model, provider: resolved.provider, proxy, maxTokens, currency: resolved.currency, inputPrice: resolved.inputPrice, outputPrice: resolved.outputPrice, cacheReadPrice: resolved.cacheReadPrice, maxContext: resolved.maxContext };\n}\n"}, "835d176cea1fbd573c06c6610b643580efa84e93": {"t": 1785759215.9302316, "n": 6315, "h": "0e60218a7722a7bc", "c": "import { readFileSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\n\nimport type { AppConfig } from '../types.js';\n\n// ---------------------------------------------------------------------------\n// Settings types — matches ~/.coder/settings.json format\n// ---------------------------------------------------------------------------\n\nexport interface ModelPrice {\n input: number;\n output: number;\n cache_read_input?: number;\n currency?: string;\n unit?: number;\n concurrency?: number;\n}\n\nexport interface ModelItem {\n name: string;\n price?: ModelPrice;\n}\n\nexport interface ModelEntry {\n /** List of model IDs available for this provider — strings or {name, price} objects */\n model: Array;\n /** Provider endpoint URL */\n base_url?: string;\n /** API key / auth token */\n auth_token_env?: string;\n /** HTTP/HTTPS proxy URL */\n proxy?: string;\n /** Maximum output tokens for this provider */\n max_tokens?: number;\n /** Provider identifier (anthropic, deepseek, openai, etc.) */\n provider?: string;\n}\n\nexport interface CoderSettings {\n env?: Record;\n model_list?: ModelEntry[];\n /** Format: \"provider/model-name\" (e.g. \"deepseek/deepseek-v4-pro\") */\n default_model?: string;\n /** Global max output tokens (default: 32768) */\n max_tokens?: number;\n /** UI theme (dark / light) */\n theme?: string;\n /** Max concurrent tool executions (default: 32, range: 1-256). */\n max_tool_concurrency?: number;\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nexport function inferProvider(model: string): string {\n const lower = model.toLowerCase();\n if (lower.includes('deepseek')) return 'deepseek';\n if (lower.includes('openai') || lower.includes('gpt') || lower.includes('o1') || lower.includes('o3') || lower.includes('o4')) return 'openai';\n return 'anthropic';\n}\n\nexport function loadSettings(): CoderSettings {\n try {\n const raw = readFileSync(join(homedir(), '.coder', 'settings.json'), 'utf-8');\n return JSON.parse(raw) as CoderSettings;\n } catch {\n return {};\n }\n}\n\n/** Resolve max tool concurrency from settings, with bounds checking. */\nexport function getMaxToolConcurrency(settings?: CoderSettings): number {\n const val = settings?.max_tool_concurrency;\n if (typeof val === 'number' && val >= 1 && val <= 256) return val;\n return 32;\n}\n\n// ---------------------------------------------------------------------------\n// Model resolution — matches KodeAgent priority:\n// 1. CODER_MODEL env var (highest)\n// 2. default_model parsed as \"provider/model-name\", looked up in model_list\n// 3. First entry in model_list\n// 4. Legacy env vars in settings.env\n// ---------------------------------------------------------------------------\n\nfunction resolveModel(settings: CoderSettings): {\n model: string;\n baseUrl: string;\n apiKey: string;\n proxy?: string;\n maxTokens?: number;\n provider: string;\n currency?: string;\n inputPrice?: number;\n outputPrice?: number;\n cacheReadPrice?: number;\n} {\n const parseDefault = (raw: string) => {\n const parts = raw.split('/');\n return { providerName: parts[0]!, modelName: parts.length > 1 ? parts[1] : undefined };\n };\n\n const modelName = (m: string | ModelItem): string =>\n typeof m === 'string' ? m : m.name;\n\n const modelPrice = (m: string | ModelItem): ModelPrice | undefined =>\n typeof m === 'string' ? undefined : m.price;\n\n const resolveFromEntry = (entry: ModelEntry, preferredModel?: string) => {\n const list = entry.model;\n const found = preferredModel\n ? list.find(m => modelName(m) === preferredModel) ?? list[0]\n : list[0];\n const selectedModel = modelName(found!);\n const price = found ? modelPrice(found) : undefined;\n return {\n model: selectedModel,\n baseUrl: entry.base_url ?? '',\n apiKey: entry.auth_token_env ?? '',\n proxy: entry.proxy,\n maxTokens: entry.max_tokens,\n provider: entry.provider ?? inferProvider(selectedModel),\n currency: price?.currency,\n inputPrice: price?.input,\n outputPrice: price?.output,\n cacheReadPrice: price?.cache_read_input,\n };\n };\n\n // 1. default_model from settings (\"provider/model-name\" format)\n const defaultName = settings.default_model;\n if (defaultName && settings.model_list) {\n const { providerName, modelName } = parseDefault(defaultName);\n const entry = settings.model_list.find(m => m.provider === providerName);\n if (entry && entry.model.length > 0) {\n return resolveFromEntry(entry, modelName);\n }\n }\n\n // 2. First entry in model_list\n if (settings.model_list && settings.model_list.length > 0) {\n const entry = settings.model_list[0]!;\n if (entry.model.length > 0) {\n return resolveFromEntry(entry);\n }\n }\n\n throw new Error(\n 'No model configured. Add model_list to ~/.coder/settings.json.',\n );\n}\n\n// ---------------------------------------------------------------------------\n// Public API — loadConfig\n// ---------------------------------------------------------------------------\n\n/**\n * Load AI model configuration from ~/.coder/settings.json.\n *\n * Resolution priority:\n * 1. default_model — \"provider/model-name\" format, looked up in model_list\n * 2. First entry in model_list\n *\n * Settings format:\n * {\n * \"model_list\": [\n * { \"model\": [\"deepseek-v4-pro\"], \"provider\": \"deepseek\",\n * \"base_url\": \"https://api.deepseek.com/anthropic\", \"auth_token_env\": \"sk-...\" }\n * ],\n * \"default_model\": \"deepseek/deepseek-v4-pro\"\n * }\n */\nexport function loadConfig(): AppConfig {\n const settings = loadSettings();\n\n const resolved = resolveModel(settings);\n\n const model = resolved.model;\n const apiKey = resolved.apiKey;\n const baseUrl = resolved.baseUrl;\n const proxy = resolved.proxy;\n const maxTokens = resolved.maxTokens ?? settings.max_tokens;\n\n if (!model) {\n throw new Error(\n 'No model configured. Set default_model in ~/.coder/settings.json.',\n );\n }\n\n return { baseUrl, apiKey, model, provider: resolved.provider, proxy, maxTokens, currency: resolved.currency, inputPrice: resolved.inputPrice, outputPrice: resolved.outputPrice, cacheReadPrice: resolved.cacheReadPrice };\n}\n"}, "675f898cb208c5e4a5751ba3422e14c92a346b4f": {"t": 1785759217.207622, "n": 5327, "h": "8afb00558bf5a79c", "c": "import { readFileSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\n\nimport type { AppConfig } from '../types.js';\n\n// ---------------------------------------------------------------------------\n// Settings types — matches ~/.coder/settings.json format\n// ---------------------------------------------------------------------------\n\nexport interface ModelEntry {\n /** List of model IDs available for this provider */\n model: string[];\n /** Provider endpoint URL */\n base_url?: string;\n /** API key / auth token */\n auth_token_env?: string;\n /** HTTP/HTTPS proxy URL */\n proxy?: string;\n /** Maximum output tokens for this provider */\n max_tokens?: number;\n /** Provider identifier (anthropic, deepseek, openai, etc.) */\n provider?: string;\n}\n\nexport interface CoderSettings {\n env?: Record;\n model_list?: ModelEntry[];\n /** Format: \"provider/model-name\" (e.g. \"deepseek/deepseek-v4-pro\") */\n default_model?: string;\n /** Global max output tokens (default: 32768) */\n max_tokens?: number;\n /** UI theme (dark / light) */\n theme?: string;\n /** Max concurrent tool executions (default: 32, range: 1-256). */\n max_tool_concurrency?: number;\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nexport function inferProvider(model: string): string {\n const lower = model.toLowerCase();\n if (lower.includes('deepseek')) return 'deepseek';\n if (lower.includes('openai') || lower.includes('gpt') || lower.includes('o1') || lower.includes('o3') || lower.includes('o4')) return 'openai';\n return 'anthropic';\n}\n\nexport function loadSettings(): CoderSettings {\n try {\n const raw = readFileSync(join(homedir(), '.coder', 'settings.json'), 'utf-8');\n return JSON.parse(raw) as CoderSettings;\n } catch {\n return {};\n }\n}\n\n/** Resolve max tool concurrency from settings, with bounds checking. */\nexport function getMaxToolConcurrency(settings?: CoderSettings): number {\n const val = settings?.max_tool_concurrency;\n if (typeof val === 'number' && val >= 1 && val <= 256) return val;\n return 32;\n}\n\n// ---------------------------------------------------------------------------\n// Model resolution — matches KodeAgent priority:\n// 1. CODER_MODEL env var (highest)\n// 2. default_model parsed as \"provider/model-name\", looked up in model_list\n// 3. First entry in model_list\n// 4. Legacy env vars in settings.env\n// ---------------------------------------------------------------------------\n\nfunction resolveModel(settings: CoderSettings): {\n model: string;\n baseUrl: string;\n apiKey: string;\n proxy?: string;\n maxTokens?: number;\n provider: string;\n} {\n const parseDefault = (raw: string) => {\n const parts = raw.split('/');\n return { providerName: parts[0]!, modelName: parts.length > 1 ? parts[1] : undefined };\n };\n\n const resolveFromEntry = (entry: ModelEntry, preferredModel?: string) => {\n const selectedModel = preferredModel\n ? (entry.model.find(m => m === preferredModel) ?? entry.model[0]!)\n : entry.model[0]!;\n return {\n model: selectedModel,\n baseUrl: entry.base_url ?? '',\n apiKey: entry.auth_token_env ?? '',\n proxy: entry.proxy,\n maxTokens: entry.max_tokens,\n provider: entry.provider ?? inferProvider(selectedModel),\n };\n };\n\n // 1. default_model from settings (\"provider/model-name\" format)\n const defaultName = settings.default_model;\n if (defaultName && settings.model_list) {\n const { providerName, modelName } = parseDefault(defaultName);\n const entry = settings.model_list.find(m => m.provider === providerName);\n if (entry && entry.model.length > 0) {\n return resolveFromEntry(entry, modelName);\n }\n }\n\n // 2. First entry in model_list\n if (settings.model_list && settings.model_list.length > 0) {\n const entry = settings.model_list[0]!;\n if (entry.model.length > 0) {\n return resolveFromEntry(entry);\n }\n }\n\n throw new Error(\n 'No model configured. Add model_list to ~/.coder/settings.json.',\n );\n}\n\n// ---------------------------------------------------------------------------\n// Public API — loadConfig\n// ---------------------------------------------------------------------------\n\n/**\n * Load AI model configuration from ~/.coder/settings.json.\n *\n * Resolution priority:\n * 1. default_model — \"provider/model-name\" format, looked up in model_list\n * 2. First entry in model_list\n *\n * Settings format:\n * {\n * \"model_list\": [\n * { \"model\": [\"deepseek-v4-pro\"], \"provider\": \"deepseek\",\n * \"base_url\": \"https://api.deepseek.com/anthropic\", \"auth_token_env\": \"sk-...\" }\n * ],\n * \"default_model\": \"deepseek/deepseek-v4-pro\"\n * }\n */\nexport function loadConfig(): AppConfig {\n const settings = loadSettings();\n\n const resolved = resolveModel(settings);\n\n const model = resolved.model;\n const apiKey = resolved.apiKey;\n const baseUrl = resolved.baseUrl;\n const proxy = resolved.proxy;\n const maxTokens = resolved.maxTokens ?? settings.max_tokens;\n\n if (!model) {\n throw new Error(\n 'No model configured. Set default_model in ~/.coder/settings.json.',\n );\n }\n\n return { baseUrl, apiKey, model, provider: resolved.provider, proxy, maxTokens };\n}\n"}, "c89947041a9a14f22d0726721edb11e9e07c2943": {"t": 1785759218.5522528, "n": 6598, "h": "c8ca6662e3e77339", "c": "import { readFileSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\n\nimport type { AppConfig } from '../types.js';\n\n// ---------------------------------------------------------------------------\n// Settings types — matches ~/.coder/settings.json format\n// ---------------------------------------------------------------------------\n\nexport interface ModelEntry {\n /** List of model IDs available for this provider */\n model: string[];\n /** Provider endpoint URL */\n base_url?: string;\n /** API key / auth token */\n auth_token_env?: string;\n /** HTTP/HTTPS proxy URL */\n proxy?: string;\n /** Maximum output tokens for this provider */\n max_tokens?: number;\n /** Provider identifier (anthropic, deepseek, openai, etc.) */\n provider?: string;\n}\n\nexport interface CoderSettings {\n env?: Record;\n model_list?: ModelEntry[];\n /** Format: \"provider/model-name\" (e.g. \"deepseek/deepseek-v4-pro\") */\n default_model?: string;\n /** Global max output tokens (default: 32768) */\n max_tokens?: number;\n /** UI theme (dark / light) */\n theme?: string;\n /** Max concurrent tool executions (default: 32, range: 1-256). */\n max_tool_concurrency?: number;\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nexport function inferProvider(model: string): string {\n const lower = model.toLowerCase();\n if (lower.includes('deepseek')) return 'deepseek';\n if (lower.includes('openai') || lower.includes('gpt') || lower.includes('o1') || lower.includes('o3') || lower.includes('o4')) return 'openai';\n return 'anthropic';\n}\n\nexport function loadSettings(): CoderSettings {\n try {\n const raw = readFileSync(join(homedir(), '.coder', 'settings.json'), 'utf-8');\n return JSON.parse(raw) as CoderSettings;\n } catch {\n return {};\n }\n}\n\n/** Resolve max tool concurrency from settings, with bounds checking. */\nexport function getMaxToolConcurrency(settings?: CoderSettings): number {\n const val = settings?.max_tool_concurrency;\n if (typeof val === 'number' && val >= 1 && val <= 256) return val;\n return 32;\n}\n\n// ---------------------------------------------------------------------------\n// Model resolution — matches KodeAgent priority:\n// 1. CODER_MODEL env var (highest)\n// 2. default_model parsed as \"provider/model-name\", looked up in model_list\n// 3. First entry in model_list\n// 4. Legacy env vars in settings.env\n// ---------------------------------------------------------------------------\n\nfunction resolveModel(settings: CoderSettings): {\n model: string;\n baseUrl: string;\n apiKey: string;\n proxy?: string;\n maxTokens?: number;\n provider: string;\n} {\n const parseDefault = (raw: string) => {\n const parts = raw.split('/');\n return { providerName: parts[0]!, modelName: parts.length > 1 ? parts[1] : undefined };\n };\n\n const resolveFromEntry = (entry: ModelEntry, preferredModel?: string) => {\n const selectedModel = preferredModel\n ? (entry.model.find(m => m === preferredModel) ?? entry.model[0]!)\n : entry.model[0]!;\n return {\n model: selectedModel,\n baseUrl: entry.base_url ?? '',\n apiKey: entry.auth_token_env ?? '',\n proxy: entry.proxy,\n maxTokens: entry.max_tokens,\n provider: entry.provider ?? inferProvider(selectedModel),\n };\n };\n\n // 1. CODER_MODEL env var\n const coderModel = process.env.CODER_MODEL;\n if (coderModel) {\n if (settings.model_list) {\n const { providerName, modelName } = parseDefault(coderModel);\n const entry = settings.model_list.find(m => m.provider === providerName);\n if (entry && entry.model.length > 0) {\n return resolveFromEntry(entry, modelName);\n }\n for (const entry of settings.model_list) {\n const matched = entry.model.find(m => m === coderModel);\n if (matched) {\n return resolveFromEntry(entry, matched);\n }\n }\n }\n return {\n model: coderModel,\n baseUrl: '',\n apiKey: '',\n provider: inferProvider(coderModel),\n };\n }\n\n // 2. default_model from settings\n const defaultName = settings.default_model;\n if (defaultName && settings.model_list) {\n const { providerName, modelName } = parseDefault(defaultName);\n const entry = settings.model_list.find(m => m.provider === providerName);\n if (entry && entry.model.length > 0) {\n return resolveFromEntry(entry, modelName);\n }\n }\n\n // 3. First entry in model_list\n if (settings.model_list && settings.model_list.length > 0) {\n const entry = settings.model_list[0]!;\n if (entry.model.length > 0) {\n return resolveFromEntry(entry);\n }\n }\n\n // 4. Legacy env fallback\n const env = settings.env ?? {};\n return {\n model: env.CODER_MODEL ?? 'claude-sonnet-4-6',\n baseUrl: env.CODER_BASE_URL ?? '',\n apiKey: env.CODER_AUTH_TOKEN ?? '',\n provider: 'anthropic',\n };\n}\n\n// ---------------------------------------------------------------------------\n// Public API — loadConfig\n// ---------------------------------------------------------------------------\n\n/**\n * Load AI model configuration from ~/.coder/settings.json.\n *\n * Supports KodeAgent-compatible format:\n * {\n * \"model_list\": [\n * { \"model\": [\"deepseek-v4-pro\"], \"provider\": \"deepseek\",\n * \"base_url\": \"https://api.deepseek.com/anthropic\", \"auth_token_env\": \"sk-...\" }\n * ],\n * \"default_model\": \"deepseek/deepseek-v4-pro\"\n * }\n *\n * Also supports legacy format:\n * {\n * \"env\": {\n * \"CODER_BASE_URL\": \"...\",\n * \"CODER_AUTH_TOKEN\": \"...\",\n * \"CODER_MODEL\": \"...\"\n * }\n * }\n */\nexport function loadConfig(): AppConfig {\n const settings = loadSettings();\n\n const resolved = resolveModel(settings);\n\n // Env vars override file config\n const apiKey =\n process.env.CODER_AUTH_TOKEN ??\n resolved.apiKey ??\n '';\n\n const baseUrl =\n process.env.CODER_BASE_URL ??\n resolved.baseUrl ??\n '';\n\n const model =\n process.env.CODER_MODEL ??\n resolved.model ??\n '';\n\n const proxy =\n process.env.CODER_PROXY ??\n resolved.proxy;\n\n const maxTokens = (() => {\n if (process.env.CODER_MAX_TOKENS) {\n const n = parseInt(process.env.CODER_MAX_TOKENS, 10);\n if (!isNaN(n)) return n;\n }\n return resolved.maxTokens ?? settings.max_tokens;\n })();\n\n if (!model) {\n throw new Error(\n 'No model configured. Set CODER_MODEL in ~/.coder/settings.json (model_list) or env.',\n );\n }\n\n return { baseUrl, apiKey, model, provider: resolved.provider, proxy, maxTokens };\n}\n"}, "f4afb695404a6c912d34d505a74000200f0b2eff": {"t": 1785759220.0975258, "n": 6211, "h": "7e4c46cd5ad224cd", "c": "import { readFileSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\n\nimport type { AppConfig } from '../types.js';\n\n// ---------------------------------------------------------------------------\n// Settings types — matches ~/.coder/settings.json format\n// ---------------------------------------------------------------------------\n\nexport interface ModelEntry {\n /** List of model IDs available for this provider */\n model: string[];\n /** Provider endpoint URL */\n base_url?: string;\n /** API key / auth token */\n auth_token_env?: string;\n /** HTTP/HTTPS proxy URL */\n proxy?: string;\n /** Maximum output tokens for this provider */\n max_tokens?: number;\n /** Provider identifier (anthropic, deepseek, openai, etc.) */\n provider?: string;\n}\n\nexport interface CoderSettings {\n env?: Record;\n model_list?: ModelEntry[];\n /** Format: \"provider/model-name\" (e.g. \"deepseek/deepseek-v4-pro\") */\n default_model?: string;\n /** Global max output tokens (default: 32768) */\n max_tokens?: number;\n /** UI theme (dark / light) */\n theme?: string;\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nexport function inferProvider(model: string): string {\n const lower = model.toLowerCase();\n if (lower.includes('deepseek')) return 'deepseek';\n if (lower.includes('openai') || lower.includes('gpt') || lower.includes('o1') || lower.includes('o3') || lower.includes('o4')) return 'openai';\n return 'anthropic';\n}\n\nfunction loadSettings(): CoderSettings {\n try {\n const raw = readFileSync(join(homedir(), '.coder', 'settings.json'), 'utf-8');\n return JSON.parse(raw) as CoderSettings;\n } catch {\n return {};\n }\n}\n\n// ---------------------------------------------------------------------------\n// Model resolution — matches KodeAgent priority:\n// 1. CODER_MODEL env var (highest)\n// 2. default_model parsed as \"provider/model-name\", looked up in model_list\n// 3. First entry in model_list\n// 4. Legacy env vars in settings.env\n// ---------------------------------------------------------------------------\n\nfunction resolveModel(settings: CoderSettings): {\n model: string;\n baseUrl: string;\n apiKey: string;\n proxy?: string;\n maxTokens?: number;\n provider: string;\n} {\n const parseDefault = (raw: string) => {\n const parts = raw.split('/');\n return { providerName: parts[0]!, modelName: parts.length > 1 ? parts[1] : undefined };\n };\n\n const resolveFromEntry = (entry: ModelEntry, preferredModel?: string) => {\n const selectedModel = preferredModel\n ? (entry.model.find(m => m === preferredModel) ?? entry.model[0]!)\n : entry.model[0]!;\n return {\n model: selectedModel,\n baseUrl: entry.base_url ?? '',\n apiKey: entry.auth_token_env ?? '',\n proxy: entry.proxy,\n maxTokens: entry.max_tokens,\n provider: entry.provider ?? inferProvider(selectedModel),\n };\n };\n\n // 1. CODER_MODEL env var\n const coderModel = process.env.CODER_MODEL;\n if (coderModel) {\n if (settings.model_list) {\n const { providerName, modelName } = parseDefault(coderModel);\n const entry = settings.model_list.find(m => m.provider === providerName);\n if (entry && entry.model.length > 0) {\n return resolveFromEntry(entry, modelName);\n }\n for (const entry of settings.model_list) {\n const matched = entry.model.find(m => m === coderModel);\n if (matched) {\n return resolveFromEntry(entry, matched);\n }\n }\n }\n return {\n model: coderModel,\n baseUrl: '',\n apiKey: '',\n provider: inferProvider(coderModel),\n };\n }\n\n // 2. default_model from settings\n const defaultName = settings.default_model;\n if (defaultName && settings.model_list) {\n const { providerName, modelName } = parseDefault(defaultName);\n const entry = settings.model_list.find(m => m.provider === providerName);\n if (entry && entry.model.length > 0) {\n return resolveFromEntry(entry, modelName);\n }\n }\n\n // 3. First entry in model_list\n if (settings.model_list && settings.model_list.length > 0) {\n const entry = settings.model_list[0]!;\n if (entry.model.length > 0) {\n return resolveFromEntry(entry);\n }\n }\n\n // 4. Legacy env fallback\n const env = settings.env ?? {};\n return {\n model: env.CODER_MODEL ?? 'claude-sonnet-4-6',\n baseUrl: env.CODER_BASE_URL ?? '',\n apiKey: env.CODER_AUTH_TOKEN ?? '',\n provider: 'anthropic',\n };\n}\n\n// ---------------------------------------------------------------------------\n// Public API — loadConfig\n// ---------------------------------------------------------------------------\n\n/**\n * Load AI model configuration from ~/.coder/settings.json.\n *\n * Supports KodeAgent-compatible format:\n * {\n * \"model_list\": [\n * { \"model\": [\"deepseek-v4-pro\"], \"provider\": \"deepseek\",\n * \"base_url\": \"https://api.deepseek.com/anthropic\", \"auth_token_env\": \"sk-...\" }\n * ],\n * \"default_model\": \"deepseek/deepseek-v4-pro\"\n * }\n *\n * Also supports legacy format:\n * {\n * \"env\": {\n * \"CODER_BASE_URL\": \"...\",\n * \"CODER_AUTH_TOKEN\": \"...\",\n * \"CODER_MODEL\": \"...\"\n * }\n * }\n */\nexport function loadConfig(): AppConfig {\n const settings = loadSettings();\n\n const resolved = resolveModel(settings);\n\n // Env vars override file config\n const apiKey =\n process.env.CODER_AUTH_TOKEN ??\n resolved.apiKey ??\n '';\n\n const baseUrl =\n process.env.CODER_BASE_URL ??\n resolved.baseUrl ??\n '';\n\n const model =\n process.env.CODER_MODEL ??\n resolved.model ??\n '';\n\n const proxy =\n process.env.CODER_PROXY ??\n resolved.proxy;\n\n const maxTokens = (() => {\n if (process.env.CODER_MAX_TOKENS) {\n const n = parseInt(process.env.CODER_MAX_TOKENS, 10);\n if (!isNaN(n)) return n;\n }\n return resolved.maxTokens ?? settings.max_tokens;\n })();\n\n if (!model) {\n throw new Error(\n 'No model configured. Set CODER_MODEL in ~/.coder/settings.json (model_list) or env.',\n );\n }\n\n return { baseUrl, apiKey, model, provider: resolved.provider, proxy, maxTokens };\n}\n"}}}}