@freeclaude/cli 3.0.6 → 3.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/cli.bundle.mjs +1262 -1036
- package/dist/cli.bundle.mjs.map +9 -8
- package/dist/cli.mjs +136 -38
- package/package.json +1 -1
package/dist/cli.mjs
CHANGED
|
@@ -8,7 +8,7 @@ import { spawnSync } from 'node:child_process'
|
|
|
8
8
|
import { homedir } from 'node:os'
|
|
9
9
|
import { join } from 'node:path'
|
|
10
10
|
|
|
11
|
-
const CONFIG_PATH = join(homedir(), '.freeclaude.json')
|
|
11
|
+
const CONFIG_PATH = process.env.FREECLAUDE_CONFIG_PATH || join(homedir(), '.freeclaude.json')
|
|
12
12
|
const CLAUDE_SETTINGS_PATH = join(homedir(), '.claude', 'settings.json')
|
|
13
13
|
const WHISPER_MODEL_PATH = join(
|
|
14
14
|
homedir(),
|
|
@@ -18,6 +18,128 @@ const WHISPER_MODEL_PATH = join(
|
|
|
18
18
|
'ggml-small.bin',
|
|
19
19
|
)
|
|
20
20
|
|
|
21
|
+
const KNOWN_PROVIDER_DEFINITIONS = [
|
|
22
|
+
{ slug: 'zai', baseUrl: 'https://api.z.ai/api/coding/paas/v4', models: ['glm-5', 'glm-4.7-flash', 'glm-4.7'] },
|
|
23
|
+
{ slug: 'gemini', baseUrl: 'https://generativelanguage.googleapis.com/v1beta/openai', models: ['gemini-2.5-flash', 'gemini-2.5-pro', 'gemini-2.5-flash-lite'] },
|
|
24
|
+
{ slug: 'groq', baseUrl: 'https://api.groq.com/openai/v1', models: ['llama-3.3-70b-versatile', 'llama-4-scout-17b-16e-instruct', 'qwen-qwq-32b'] },
|
|
25
|
+
{ slug: 'cerebras', baseUrl: 'https://api.cerebras.ai/v1', models: ['llama-4-scout-17b-16e', 'llama3.1-8b', 'qwen-2.5-32b'] },
|
|
26
|
+
{ slug: 'siliconflow', baseUrl: 'https://api.siliconflow.cn/v1', models: ['deepseek-ai/DeepSeek-V3', 'Qwen/Qwen3-8B'] },
|
|
27
|
+
{ slug: 'qwen', baseUrl: 'https://dashscope.aliyuncs.com/compatible-mode/v1', models: ['qwen3-235b-a22b', 'qwen-max', 'qwen-plus', 'qwen-turbo', 'qwen-coder-plus'] },
|
|
28
|
+
{ slug: 'sambanova', baseUrl: 'https://api.sambanova.ai/v1', models: ['Meta-Llama-3.3-70B-Instruct', 'DeepSeek-R1-Distill-Llama-70B'] },
|
|
29
|
+
{ slug: 'ollama', baseUrl: 'http://localhost:11434/v1', models: ['qwen2.5:3b', 'qwen2.5:7b', 'llama3.2', 'deepseek-r1:8b'] },
|
|
30
|
+
{ slug: 'lmstudio', baseUrl: 'http://localhost:1234/v1', models: ['(auto-detected)'] },
|
|
31
|
+
{ slug: 'openai', baseUrl: 'https://api.openai.com/v1', models: ['gpt-4o', 'gpt-4o-mini', 'o3-mini'] },
|
|
32
|
+
{ slug: 'deepseek', baseUrl: 'https://api.deepseek.com/v1', models: ['deepseek-chat', 'deepseek-reasoner', 'deepseek-coder'] },
|
|
33
|
+
{ slug: 'mistral', baseUrl: 'https://api.mistral.ai/v1', models: ['mistral-large-latest', 'codestral-latest'] },
|
|
34
|
+
{ slug: 'openrouter', baseUrl: 'https://openrouter.ai/api/v1', models: ['anthropic/claude-sonnet-4', 'google/gemini-2.5-flash', 'openai/gpt-4o', 'deepseek/deepseek-chat'] },
|
|
35
|
+
{ slug: 'together', baseUrl: 'https://api.together.xyz/v1', models: ['meta-llama/Llama-4-Maverick-17B-128E-Instruct', 'Qwen/Qwen3-235B-A22B'] },
|
|
36
|
+
{ slug: 'fireworks', baseUrl: 'https://api.fireworks.ai/inference/v1', models: ['accounts/fireworks/models/llama-v4p-70b-instruct'] },
|
|
37
|
+
{ slug: 'deepinfra', baseUrl: 'https://api.deepinfra.com/v1/openai', models: ['meta-llama/Llama-4-Maverick-17B-128E-Instruct', 'Qwen/Qwen3-235B-A22B'] },
|
|
38
|
+
{ slug: 'perplexity', baseUrl: 'https://api.perplexity.ai', models: ['sonar-pro', 'sonar-reasoning'] },
|
|
39
|
+
{ slug: 'huggingface', baseUrl: 'https://api-inference.huggingface.co/v1', models: ['(any HF model ID)'] },
|
|
40
|
+
]
|
|
41
|
+
|
|
42
|
+
function normalizeBaseUrl(baseUrl) {
|
|
43
|
+
return (baseUrl || '').trim().replace(/\/+$/, '').toLowerCase()
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function normalizeName(name) {
|
|
47
|
+
return (name || '').trim().toLowerCase()
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function findKnownProviderDefinition(provider) {
|
|
51
|
+
const baseUrl = normalizeBaseUrl(provider?.baseUrl)
|
|
52
|
+
if (baseUrl) {
|
|
53
|
+
const byBaseUrl = KNOWN_PROVIDER_DEFINITIONS.find(entry => normalizeBaseUrl(entry.baseUrl) === baseUrl)
|
|
54
|
+
if (byBaseUrl) return byBaseUrl
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const name = normalizeName(provider?.name)
|
|
58
|
+
if (!name) return undefined
|
|
59
|
+
return KNOWN_PROVIDER_DEFINITIONS.find(entry => normalizeName(entry.slug) === name)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function resolveConfiguredModel(provider, model) {
|
|
63
|
+
const trimmedModel = typeof model === 'string' ? model.trim() : ''
|
|
64
|
+
const knownProvider = findKnownProviderDefinition(provider)
|
|
65
|
+
|
|
66
|
+
if (!trimmedModel) {
|
|
67
|
+
return knownProvider?.models?.[0]
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (!knownProvider) {
|
|
71
|
+
return trimmedModel
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (/^\d+$/.test(trimmedModel)) {
|
|
75
|
+
const index = Number.parseInt(trimmedModel, 10) - 1
|
|
76
|
+
if (index >= 0 && index < knownProvider.models.length) {
|
|
77
|
+
return knownProvider.models[index]
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return trimmedModel
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function normalizeConfig(config) {
|
|
85
|
+
const providers = Array.isArray(config?.providers)
|
|
86
|
+
? config.providers.map(provider => ({ ...provider }))
|
|
87
|
+
: []
|
|
88
|
+
let changed = false
|
|
89
|
+
|
|
90
|
+
for (const provider of providers) {
|
|
91
|
+
const resolvedModel = resolveConfiguredModel(provider, provider.model)
|
|
92
|
+
if (resolvedModel && resolvedModel !== provider.model) {
|
|
93
|
+
provider.model = resolvedModel
|
|
94
|
+
changed = true
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const normalized = { ...config, providers }
|
|
99
|
+
const activeProvider = normalized.activeProvider
|
|
100
|
+
? providers.find(provider => provider.name === normalized.activeProvider)
|
|
101
|
+
: providers.length === 1
|
|
102
|
+
? providers[0]
|
|
103
|
+
: undefined
|
|
104
|
+
const resolvedActiveModel = resolveConfiguredModel(
|
|
105
|
+
activeProvider ?? { name: normalized.activeProvider },
|
|
106
|
+
normalized.activeModel,
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
if (
|
|
110
|
+
resolvedActiveModel &&
|
|
111
|
+
normalized.activeModel &&
|
|
112
|
+
resolvedActiveModel !== normalized.activeModel
|
|
113
|
+
) {
|
|
114
|
+
normalized.activeModel = resolvedActiveModel
|
|
115
|
+
changed = true
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return { config: normalized, changed }
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function getOrderedConfiguredProviders(config) {
|
|
122
|
+
const providers = [...(config.providers || [])].sort(
|
|
123
|
+
(a, b) => (a.priority ?? 99) - (b.priority ?? 99),
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
if (!config.activeProvider) {
|
|
127
|
+
return providers
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const activeIdx = providers.findIndex(provider => provider.name === config.activeProvider)
|
|
131
|
+
if (activeIdx < 0) {
|
|
132
|
+
return providers
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const [activeProvider] = providers.splice(activeIdx, 1)
|
|
136
|
+
if (activeProvider && config.activeModel) {
|
|
137
|
+
activeProvider.model = resolveConfiguredModel(activeProvider, config.activeModel) || activeProvider.model
|
|
138
|
+
}
|
|
139
|
+
providers.unshift(activeProvider)
|
|
140
|
+
return providers
|
|
141
|
+
}
|
|
142
|
+
|
|
21
143
|
function resolveKey(key) {
|
|
22
144
|
return (typeof key === 'string' && key.startsWith('env:'))
|
|
23
145
|
? (process.env[key.slice(4)] || '')
|
|
@@ -25,16 +147,20 @@ function resolveKey(key) {
|
|
|
25
147
|
}
|
|
26
148
|
|
|
27
149
|
function hasValidProviders() {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
const c = JSON.parse(readFileSync(CONFIG_PATH, 'utf-8'))
|
|
31
|
-
return Array.isArray(c.providers) && c.providers.some(p => resolveKey(p.apiKey))
|
|
32
|
-
} catch { return false }
|
|
150
|
+
const config = loadConfig()
|
|
151
|
+
return Array.isArray(config.providers) && config.providers.some(p => resolveKey(p.apiKey))
|
|
33
152
|
}
|
|
34
153
|
|
|
35
154
|
function loadConfig() {
|
|
36
155
|
if (!existsSync(CONFIG_PATH)) return {}
|
|
37
|
-
try {
|
|
156
|
+
try {
|
|
157
|
+
const config = JSON.parse(readFileSync(CONFIG_PATH, 'utf-8'))
|
|
158
|
+
const normalized = normalizeConfig(config)
|
|
159
|
+
if (normalized.changed) {
|
|
160
|
+
saveConfig(normalized.config)
|
|
161
|
+
}
|
|
162
|
+
return normalized.config
|
|
163
|
+
}
|
|
38
164
|
catch { return {} }
|
|
39
165
|
}
|
|
40
166
|
|
|
@@ -50,24 +176,7 @@ function applyConfig() {
|
|
|
50
176
|
const config = loadConfig()
|
|
51
177
|
if (!Array.isArray(config.providers)) return
|
|
52
178
|
|
|
53
|
-
const
|
|
54
|
-
|
|
55
|
-
// 1. Try activeProvider first (remembers last choice)
|
|
56
|
-
if (config.activeProvider) {
|
|
57
|
-
const active = providers.find(p => p.name === config.activeProvider && p.model === config.activeModel)
|
|
58
|
-
if (active) {
|
|
59
|
-
const key = resolveKey(active.apiKey)
|
|
60
|
-
if (key) {
|
|
61
|
-
process.env.OPENAI_API_KEY = key
|
|
62
|
-
process.env.OPENAI_BASE_URL = active.baseUrl
|
|
63
|
-
process.env.OPENAI_MODEL = active.model
|
|
64
|
-
return
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
// 2. Fallback: first valid provider by priority
|
|
70
|
-
for (const p of [...providers].sort((a, b) => (a.priority ?? 99) - (b.priority ?? 99))) {
|
|
179
|
+
for (const p of getOrderedConfiguredProviders(config)) {
|
|
71
180
|
const key = resolveKey(p.apiKey)
|
|
72
181
|
if (key) {
|
|
73
182
|
process.env.OPENAI_API_KEY = key
|
|
@@ -102,18 +211,7 @@ function getPrimaryConfiguredProvider() {
|
|
|
102
211
|
return null
|
|
103
212
|
}
|
|
104
213
|
|
|
105
|
-
|
|
106
|
-
const active = config.providers.find(
|
|
107
|
-
p => p.name === config.activeProvider && p.model === config.activeModel,
|
|
108
|
-
)
|
|
109
|
-
if (active && (resolveKey(active.apiKey) || isLocalProviderUrl(active.baseUrl))) {
|
|
110
|
-
return active
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
for (const provider of [...config.providers].sort(
|
|
115
|
-
(a, b) => (a.priority ?? 99) - (b.priority ?? 99),
|
|
116
|
-
)) {
|
|
214
|
+
for (const provider of getOrderedConfiguredProviders(config)) {
|
|
117
215
|
if (resolveKey(provider.apiKey) || isLocalProviderUrl(provider.baseUrl)) {
|
|
118
216
|
return provider
|
|
119
217
|
}
|
|
@@ -236,7 +334,7 @@ async function main() {
|
|
|
236
334
|
|
|
237
335
|
// --version
|
|
238
336
|
if (args.length === 1 && (args[0] === '--version' || args[0] === '-v' || args[0] === '-V')) {
|
|
239
|
-
console.log('3.0.
|
|
337
|
+
console.log('3.0.6 (FreeClaude)')
|
|
240
338
|
return
|
|
241
339
|
}
|
|
242
340
|
|