free-coding-models 0.5.78 → 0.5.80

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.
@@ -0,0 +1,12 @@
1
+ # Changelog v0.5.79 - 2026-08-23
2
+
3
+ ### Fixed
4
+ - **Fast-coding pool malformed tool_calls hang (fixes #124).** Some providers returned `finish_reason: tool_calls` without a `tool_calls` array, causing pi and other OpenAI clients to wait forever. Router now normalizes such responses to `finish_reason: stop` in `src/core/router-daemon.js` (`normalizeToolCallsResponse`) before forwarding, making the response spec-compliant even when upstream is quirky. Thanks @stgreenb.
5
+
6
+ - **Docker mandatory update EACCES crash (fixes #109).** Inside Docker the app lives at `/app` and runs as non-root `fcm`, so `npm i -g` always hit EACCES on `/usr/local/lib/node_modules` and showed "Mandatory update failed" then exited. Added `isRunningInDocker()` in `src/core/updater.js` (checks `/.dockerenv` and `/app` prefix) to skip mandatory startup updates in containers; rebuild the image to update. Thanks @karneaud.
7
+
8
+ ### Changed
9
+ - Closed older user feature requests with concise responses: #51 Nix module (PR welcome), #30 oh-my-opencode (wrapper externe, pas de duplication), #27 Claude Code integration (now via router `fcm`). Only vava's own issues (75, 21, 18) remain open.
10
+
11
+ ### Tests
12
+ - 813/813 passing. Router now re-stringifies normalized JSON only when needed.
@@ -0,0 +1,17 @@
1
+ # Changelog v0.5.80 - 2026-08-23
2
+
3
+ ### Changed
4
+ - **Catalog cleanup via @Muhammad95959 (PR #161).** Removed/updated models that are no longer in live provider APIs:
5
+ - **NVIDIA NIM**: removed `z-ai/glm-5.2`, `mistralai/mistral-medium-3.5-128b` (no longer in `integrate.api.nvidia.com/v1/models`); kept Mistral via direct LP.
6
+ - **Cerebras**: removed `zai-glm-4.7` (shutdown 2026-08-17).
7
+ - **OpenRouter**: removed `openai/gpt-oss-20b:free` (only paid remains).
8
+ - **Codestral**: removed `codestral-2501`, `codestral-2405` (only `codestral-2508`/`latest` remain).
9
+ - **ZAI**: removed `zai/glm-5v-turbo`, `zai/glm-4.7-flashx`, `zai/glm-4.6v` (now paid-only).
10
+ - **OpenCode Zen**: removed `deepseek-v4-flash-free`, `laguna-s-2.1-free` (dropped from free-tier docs).
11
+ - **Ollama Cloud**: removed `minimax-m2.5`, `gemini-3-flash-preview`, `kimi-k2.5`; fixed IDs `deepseek-v4-flash`/`pro` and `mistral-large-3`/`qwen3.5` to new tags.
12
+
13
+ ### Fixed
14
+ - **Test for NVIDIA probe list** updated to reflect new catalog head (`moonshotai/kimi-k2.6` etc.) so `pnpm test` stays green.
15
+
16
+ ### Tests
17
+ - 813/813 passing. Thanks @Muhammad95959 for the live catalog verification!
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "free-coding-models",
3
- "version": "0.5.78",
3
+ "version": "0.5.80",
4
4
  "description": "Find the fastest coding LLM models in seconds — ping free models from multiple providers, pick the best one for OpenCode, Cursor, or any AI coding assistant.",
5
5
  "keywords": [
6
6
  "nvidia",
package/sources.js CHANGED
@@ -41,8 +41,7 @@
41
41
  // 📖 NIM source - https://build.nvidia.com
42
42
  export const nvidiaNim = [
43
43
  // ── S+ tier — SWE-bench Verified ≥70% ──
44
- ['z-ai/glm-5.2', 'GLM 5.1', 'S+', '82.8%', '128k'],
45
- // Removed (2026-07-27): minimaxai/minimax-m2.7 (MiniMax M2.7) — EOL 2026-07-27 (HTTP 410 Gone)
44
+ // Removed (2026-08-23): z-ai/glm-5.2 (GLM 5.1) no longer in integrate.api.nvidia.com/v1/models (102 models live)
46
45
  ['moonshotai/kimi-k2.6', 'Kimi K2.6', 'S+', '80.2%', '262k'],
47
46
  ['deepseek-ai/deepseek-v4-pro', 'DeepSeek V4 Pro', 'S+', '80.6%', '1M'], // ⚠️ Page-only / partner-routed (2026-08-13): listed on build.nvidia.com but NOT in integrate.api.nvidia.com/v1/models; served via Fireworks/DeepInfra/Together/OpenRouter
48
47
  ['deepseek-ai/deepseek-v4-flash-0731', 'DeepSeek V4 Flash', 'S+', '79.0%', '1M'], // Fixed (2026-08-13): id 'deepseek-ai/deepseek-v4-flash' → 'deepseek-ai/deepseek-v4-flash-0731' (NIM /v1/models only exposes the -0731 suffix)
@@ -52,7 +51,7 @@ export const nvidiaNim = [
52
51
  // ── S tier — SWE-bench Verified 60–70% ──
53
52
  ['openai/gpt-oss-120b', 'GPT OSS 120B', 'S', '62.4%', '128k'],
54
53
  // Removed (2026-07-27): meta/llama-4-maverick-17b-128e-instruct (Llama 4 Maverick) — EOL 2026-07-27 (HTTP 410 Gone)
55
- ['mistralai/mistral-medium-3.5-128b', 'Mistral Medium 3.5', 'S+', '77.6%', '256k'],
54
+ // Removed (2026-08-23): mistralai/mistral-medium-3.5-128b (Mistral Medium 3.5) no longer in integrate.api.nvidia.com/v1/models (still on Mistral LP directly)
56
55
  // Removed (2026-07-27): mistralai/mistral-small-4-119b-2603 (Mistral Small 4) — EOL 2026-07-27 (HTTP 410 Gone)
57
56
  ['minimaxai/minimax-m3', 'MiniMax M3', 'S+', '78.4%', '1M'],
58
57
  ['mistralai/mistral-nemotron', 'Mistral Nemotron', 'S', '-', '128k'], // Fixed ID (2026-07-27): nvidia/mistral-nemotron → mistralai/mistral-nemotron
@@ -105,9 +104,7 @@ export const groq = [
105
104
  // 📖 Cerebras source - https://cloud.cerebras.ai
106
105
  // 📖 Free API keys available at https://cloud.cerebras.ai
107
106
  export const cerebras = [
108
- // ── S+ tierSWE-bench Verified ≥70% ──
109
- // ⚠️ DEPRECATED — shutdown 2026-08-17 (Cerebras official notice)
110
- ['zai-glm-4.7', 'GLM 4.7', 'S+', '73.8%', '64k', null, '2026-08-17'], // Fixed (2026-07-27): ctx '128k' → '64k' (free tier per official docs)
107
+ // Removed (2026-08-23): zai-glm-4.7 (GLM 4.7) shutdown 2026-08-17 per Cerebras official notice
111
108
  // ── S tier — SWE-bench Verified 60–70% ──
112
109
  ['gpt-oss-120b', 'GPT OSS 120B', 'S', '62.4%', '65k'], // Fixed (2026-07-27): ctx '128k' → '65k' (free tier per official docs)
113
110
  // ── A tier — SWE-bench Verified 40–50% ──
@@ -168,7 +165,7 @@ export const openrouter = [
168
165
  // Removed (2026-07-27): qwen/qwen3-next-80b-a3b-instruct:free (Qwen3 Next 80B A3B) — no longer on free tier
169
166
  // ── B+ tier — SWE-bench Verified 30–35% ──
170
167
  ['nvidia/nemotron-nano-9b-v2:free', 'Nemotron Nano 9B', 'B+', '18.0%', '128k'],
171
- ['openai/gpt-oss-20b:free', 'GPT OSS 20B', 'A-', '-', '131k'], // Added (2026-07-27)
168
+ // Removed (2026-08-23): openai/gpt-oss-20b:free (GPT OSS 20B) :free variant removed from OpenRouter catalog; only paid variant remains ($0.03/$0.13)
172
169
  // ── B tier — SWE-bench Verified 20–30% ──
173
170
  // Removed (2026-07-27): openrouter/free (OpenRouter Free) — no longer on free tier
174
171
  // Removed (2026-07-27): meta-llama/llama-3.2-3b-instruct:free (Llama 3.2 3B Instruct) — no longer on free tier
@@ -213,8 +210,7 @@ export const mistral = [
213
210
  export const codestral = [
214
211
  // ── A tier — SWE-bench Verified 40–50% ──
215
212
  ['codestral-2508', 'Codestral', 'A', '40.0%', '128k'], // Fixed (2026-07-27): ctx '256k' → '128k' per official Mistral model card
216
- ['codestral-2501', 'Codestral 2501', 'B+', '34.0%', '256k'], // Added (2026-08-13)
217
- ['codestral-2405', 'Codestral 2405', 'B', '30.0%', '32k'], // Added (2026-08-13)
213
+ // Removed (2026-08-23): codestral-2501 (Codestral 2501), codestral-2405 (Codestral 2405) retired from Mistral API; only codestral-2508 / codestral-latest remain
218
214
  // Removed (2026-08-13): codestral-2 (Codestral 2) — fabricated ID, never existed in Mistral catalog (Mistral uses date-stamped versioning)
219
215
  ]
220
216
 
@@ -263,6 +259,8 @@ export const googleai = [
263
259
 
264
260
  // 📖 ZAI source - https://open.z.ai
265
261
  // 📖 Free tier is limited to Flash models; paid GLM models are intentionally excluded.
262
+ // 📖 Verified live (2026-08-23) via ping test: glm-4.5-flash and glm-4.6v-flash still serve free;
263
+ // 📖 glm-4.7-flash is free but was returning "overloaded" 429s; API /models lists only 9 text models.
266
264
  export const zai = [
267
265
  // ── S+ tier — SWE-bench Verified ≥70% ──
268
266
  ['zai/glm-5.2', 'GLM-5.2', 'S+', '-', '1M'], // Added (2026-08-13)
@@ -270,13 +268,11 @@ export const zai = [
270
268
  ['zai/glm-4.7-flash', 'GLM-4.7-Flash', 'A+', '59.2%', '200k'], // Fixed (2026-07-27): ctx '203k' → '200k' per official docs
271
269
  ['zai/glm-4.5-flash', 'GLM-4.5-Flash', 'S', '59.2%', '128k'],
272
270
  ['zai/glm-5-turbo', 'GLM-5-Turbo', 'S', '-', '200k'], // Added (2026-08-13)
273
- ['zai/glm-5v-turbo', 'GLM-5V-Turbo', 'S', '-', '200k'], // Added (2026-08-13)
274
271
  ['zai/glm-4.7', 'GLM-4.7', 'S', '-', '200k'], // Added (2026-08-13)
275
272
  ['zai/glm-4.6', 'GLM-4.6', 'S', '-', '200k'], // Added (2026-08-13)
276
- ['zai/glm-4.7-flashx', 'GLM-4.7-FlashX', 'A+', '-', '200k'], // Added (2026-08-13)
273
+ // Removed (2026-08-23): zai/glm-4.7-flashx, zai/glm-5v-turbo, zai/glm-4.6v — now paid-only ("Insufficient balance or no resource package" per ping test)
277
274
  // ── A tier — SWE-bench Verified 40–50% ──
278
275
  ['zai/glm-4.6v-flash', 'GLM-4.6V-Flash', 'A', '-', '128k'],
279
- ['zai/glm-4.6v', 'GLM-4.6V', 'A', '-', '128k'], // Added (2026-08-13)
280
276
  ]
281
277
 
282
278
  // 📖 Alibaba Cloud (DashScope) source - https://dashscope-intl.aliyuncs.com
@@ -381,11 +377,11 @@ export const ovhcloud = [
381
377
  // 📖 Config: set provider to opencode/<model-id> in OpenCode config
382
378
  export const opencodeZen = [
383
379
  ['big-pickle', 'Big Pickle', 'S+', '72.0%', '200k'],
384
- ['deepseek-v4-flash-free', 'DeepSeek V4 Flash Free', 'S+', '79.0%', '200k'],
380
+ // Removed (2026-08-23): deepseek-v4-flash-free (DeepSeek V4 Flash Free) dropped from Zen free-tier docs/pricing; only paid DeepSeek V4 Flash remains
385
381
  ['mimo-v2.5-free', 'MiMo-V2.5 Free', 'S+', '-', '200k'],
386
382
  ['nemotron-3-ultra-free', 'Nemotron 3 Ultra Free', 'S+', '71.9%', '200k'],
387
383
  // Removed (2026-08-13): north-mini-code-free (North Mini Code Free) — no longer in OpenCode Zen free-tier API
388
- ['laguna-s-2.1-free', 'Laguna S 2.1 Free', 'S+', '-', '262k'], // Fixed (2026-08-13): ID 'poolside/laguna-s-2.1-free' 'laguna-s-2.1-free' (poolside/ prefix dropped)
384
+ // Removed (2026-08-23): laguna-s-2.1-free (Laguna S 2.1 Free) dropped from Zen free-tier docs/pricing (still on /models but no longer free)
389
385
  // Removed (2026-08-13): ling-3.0-flash-free (Ling-3.0-flash Free) — no longer in OpenCode Zen free-tier API
390
386
  ['hy3-free', 'Tencent Hy3 Free', 'S', '-', '200k'], // Added (2026-08-13) — brought back after July removal
391
387
  ['nemotron-3.5-lightning-free', 'Nemotron 3.5 Lightning Free','S+','-', '200k'], // Added (2026-08-13)
@@ -464,22 +460,22 @@ export const ollamaCloud = [
464
460
  ['glm-5.2', 'GLM 5.2', 'S+', '82.8%', '1M'], // Fixed (2026-07-27): ctx '128k' → '1M'
465
461
  ['minimax-m2.7', 'MiniMax M2.7', 'S+', '78.0%', '200k'],
466
462
  ['minimax-m3', 'MiniMax M3', 'S+', '78.4%', '1M'], // Fixed (2026-07-27): ctx '512k' → '1M'
467
- ['minimax-m2.5', 'MiniMax M2.5', 'S+', '80.2%', '198k'], // Fixed (2026-07-27): ctx '200k' → '198k'
463
+ // Removed (2026-08-23): minimax-m2.5 (MiniMax M2.5) no longer in ollama.com/v1/models (19 models live)
468
464
  ['kimi-k2.6', 'Kimi K2.6', 'S+', '80.2%', '256k'], // Fixed (2026-07-27): ctx '262k' → '256k'
469
- ['deepseek-v4-flash', 'DeepSeek V4 Flash', 'S+', '79.0%', '1M'], // Fixed (2026-07-27): ctx '256k' → '1M'
470
- ['deepseek-v4-pro', 'DeepSeek V4 Pro', 'S+', '80.6%', '1M'], // Fixed (2026-07-27): ctx '256k' → '1M'
465
+ ['deepseek-v4-flash:0731', 'DeepSeek V4 Flash', 'S+', '79.0%', '1M'], // Fixed (2026-08-23): ID 'deepseek-v4-flash' → 'deepseek-v4-flash:0731' (renamed upstream)
466
+ ['deepseek-v4-pro:0813', 'DeepSeek V4 Pro', 'S+', '80.6%', '1M'], // Fixed (2026-08-23): ID 'deepseek-v4-pro' → 'deepseek-v4-pro:0813' (renamed upstream)
471
467
  // ── S tier — SWE-bench Verified 60–70% ──
472
468
  ['kimi-k2.7-code', 'Kimi K2.7 Code', 'S', '60.4%', '256k'], // Fixed (2026-07-27): ctx '262k' → '256k'
473
469
  ['gpt-oss:120b', 'GPT OSS 120B', 'S', '62.4%', '128k'],
474
470
  ['nemotron-3-super', 'Nemotron 3 Super', 'S', '60.5%', '256k'],
475
471
  ['kimi-k3', 'Kimi K3', 'S+', '-', '1M'], // Added (2026-07-27)
476
- ['gemini-3-flash-preview', 'Gemini 3 Flash Preview', 'S+', '-', '1M'], // Added (2026-07-27)
472
+ // Removed (2026-08-23): gemini-3-flash-preview (Gemini 3 Flash Preview) no gemini models left in Ollama Cloud API
477
473
  // ── A+ tier — SWE-bench Verified 50–60% ──
478
- ['kimi-k2.5', 'Kimi K2.5', 'A+', '-', '200k'],
474
+ // Removed (2026-08-23): kimi-k2.5 (Kimi K2.5) no longer in ollama.com/v1/models
479
475
  ['gemma4:31b', 'Gemma 4 31B', 'A+', '52.0%', '128k'], // Fixed (2026-07-27): ctx '256k' → '128k'
480
476
  ['gpt-oss:20b', 'GPT OSS 20B', 'A+', '50.3%', '128k'],
481
- ['mistral-large-3:675b-cloud', 'Mistral Large 3 675B Cloud', 'A+', '-', '256k'], // Added (2026-07-27)
482
- ['qwen3.5', 'Qwen 3.5 Cloud', 'A+', '-', '256k'], // Added (2026-07-27)
477
+ ['mistral-large-3:675b', 'Mistral Large 3 675B Cloud', 'A+', '-', '256k'], // Fixed (2026-08-23): ID 'mistral-large-3:675b-cloud' → 'mistral-large-3:675b' (tag renamed upstream)
478
+ ['qwen3.5:397b', 'Qwen 3.5 Cloud', 'A+', '-', '256k'], // Fixed (2026-08-23): ID 'qwen3.5' → 'qwen3.5:397b' (tag renamed upstream)
483
479
  ['nemotron-3-nano:30b', 'Nemotron 3 Nano 30B', 'A-', '38.8%', '1M'],
484
480
  ]
485
481
 
@@ -118,6 +118,32 @@ const __dirname = dirname(fileURLToPath(import.meta.url))
118
118
  const CLI_ENTRY_PATH = join(__dirname, '..', '..', 'bin', 'free-coding-models.js')
119
119
  const LOCAL_VERSION = JSON.parse(readFileSync(join(__dirname, '..', '..', 'package.json'), 'utf8')).version
120
120
  const MAX_BODY_BYTES = 10 * 1024 * 1024
121
+ /**
122
+ * 📖 normalizeToolCallsResponse — fix malformed tool_calls from upstream.
123
+ * Some providers return finish_reason: "tool_calls" but message has no tool_calls array,
124
+ * which hangs OpenAI-compatible clients (pi agent waits for tool_calls that never arrives).
125
+ * Normalize to finish_reason: "stop" when tool_calls is missing/empty per OpenAI spec.
126
+ * This is defensive: it makes the router spec-compliant regardless of upstream quirks.
127
+ * @param {object} data - parsed JSON response object
128
+ * @returns {boolean} true if mutated
129
+ */
130
+ function normalizeToolCallsResponse(data) {
131
+ if (!data || typeof data !== 'object' || !Array.isArray(data.choices)) return false
132
+ let mutated = false
133
+ for (const choice of data.choices) {
134
+ if (!choice || typeof choice !== 'object') continue
135
+ if (choice.finish_reason !== 'tool_calls') continue
136
+ const msg = choice.message
137
+ if (!msg || typeof msg !== 'object') continue
138
+ const tc = msg.tool_calls
139
+ if (!Array.isArray(tc) || tc.length === 0) {
140
+ choice.finish_reason = 'stop'
141
+ mutated = true
142
+ }
143
+ }
144
+ return mutated
145
+ }
146
+
121
147
  const MAX_REQUEST_LOG = 200
122
148
  const MAX_SSE_CLIENTS = 10
123
149
  const MAX_CONCURRENT_REQUESTS = 50
@@ -2226,13 +2252,20 @@ class RouterRuntime {
2226
2252
  failover: attemptIndex > 0,
2227
2253
  })
2228
2254
  this.logger.info(`Routed to ${key} - ${latencyMs}ms`, { request_id: requestId, status: response.status })
2255
+ // 📖 Fix #124: normalize malformed tool_calls (finish_reason tool_calls without tool_calls array)
2256
+ let responseText = text
2257
+ try {
2258
+ if (normalizeToolCallsResponse(parsed.value)) {
2259
+ responseText = JSON.stringify(parsed.value)
2260
+ }
2261
+ } catch {}
2229
2262
  if (!res.writableEnded) {
2230
2263
  res.writeHead(response.status, {
2231
2264
  ...headerEntries(response.headers),
2232
2265
  'x-fcm-router-model': key,
2233
2266
  'x-request-id': requestId,
2234
2267
  })
2235
- res.end(text)
2268
+ res.end(responseText)
2236
2269
  }
2237
2270
  return { done: true }
2238
2271
  }
@@ -110,6 +110,25 @@ export function isPackageDevMode() {
110
110
  return process.env.FCM_DEV === '1' || existsSync(join(PACKAGE_ROOT, '.git'))
111
111
  }
112
112
 
113
+ /**
114
+ * 📖 isRunningInDocker: detect Docker/container environment where global npm writes fail.
115
+ * In Docker the app lives at /app (not /usr/local/lib/node_modules) and runs as non-root `fcm`.
116
+ * Mandatory updates would always EACCES, so we skip them gracefully.
117
+ * @returns {boolean}
118
+ */
119
+ export function isRunningInDocker() {
120
+ // 📖 Standard Docker marker file, plus explicit env override for testing
121
+ if (process.env.FCM_DOCKER === '1' || process.env.DOCKER_CONTAINER === '1') return true
122
+ try {
123
+ if (existsSync('/.dockerenv')) return true
124
+ } catch {}
125
+ // 📖 Fallback: check if package is at /app (Docker COPY) vs global prefix
126
+ try {
127
+ if (String(PACKAGE_ROOT) === '/app' || String(PACKAGE_ROOT).startsWith('/app/')) return true
128
+ } catch {}
129
+ return false
130
+ }
131
+
113
132
  /**
114
133
  * 📖 getUpdateInstallFailureCount: sanitized persistent failure counter.
115
134
  * @param {object} config
@@ -265,6 +284,11 @@ export async function enforceMandatoryStartupUpdate(config, options = {}) {
265
284
  }
266
285
 
267
286
  if (devMode) return base
287
+ if (isRunningInDocker()) {
288
+ // 📖 In Docker, global npm install as non-root always EACCES. Skip mandatory update
289
+ // 📖 and let the container run with the baked-in version. User can rebuild image for updates.
290
+ return base
291
+ }
268
292
 
269
293
  const { latestVersion, error } = await checkForUpdateDetailed()
270
294
  base.checked = true