free-coding-models 0.3.3 โ 0.3.4
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/CHANGELOG.md +10 -0
- package/README.md +12 -12
- package/bin/free-coding-models.js +13 -1
- package/package.json +1 -1
- package/src/anthropic-translator.js +1 -1
- package/src/cli-help.js +2 -1
- package/src/config.js +9 -7
- package/src/opencode.js +1 -1
- package/src/proxy-server.js +26 -0
- package/src/proxy-sync.js +1 -1
- package/src/proxy-topology.js +1 -1
- package/src/tool-launchers.js +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
---
|
|
4
4
|
|
|
5
|
+
## 0.3.4
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
- **Proxy root landing JSON**: `GET /` on FCM Proxy V2 now returns a small unauthenticated status payload, so browser checks no longer fail with `{"error":"Unauthorized"}`.
|
|
9
|
+
- **`daemon stop` CLI command**: The public CLI now supports `free-coding-models daemon stop`, matching the existing daemon manager capability and the documented workflow.
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
- **README/UI parity restored**: The docs now match the current product surface, including `160` models, the `Used` token-history column, and the current launcher/proxy behavior.
|
|
13
|
+
- **Malformed config sections are normalized on load**: Invalid `apiKeys`, `providers`, or `settings` values are now coerced back to safe empty objects instead of leaking broken runtime shapes into the app.
|
|
14
|
+
|
|
5
15
|
## 0.3.3
|
|
6
16
|
|
|
7
17
|
### Fixed
|
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
<img src="https://img.shields.io/npm/v/free-coding-models?color=76b900&label=npm&logo=npm" alt="npm version">
|
|
3
3
|
<img src="https://img.shields.io/node/v/free-coding-models?color=76b900&logo=node.js" alt="node version">
|
|
4
4
|
<img src="https://img.shields.io/npm/l/free-coding-models?color=76b900" alt="license">
|
|
5
|
-
<img src="https://img.shields.io/badge/models-
|
|
5
|
+
<img src="https://img.shields.io/badge/models-160-76b900?logo=nvidia" alt="models count">
|
|
6
6
|
<img src="https://img.shields.io/badge/providers-20-blue" alt="providers count">
|
|
7
7
|
</p>
|
|
8
8
|
|
|
@@ -81,7 +81,7 @@ By Vanessa Depraute
|
|
|
81
81
|
- **๐ Rolling averages** โ Avg calculated from ALL successful pings since start
|
|
82
82
|
- **๐ Uptime tracking** โ Percentage of successful pings shown in real-time
|
|
83
83
|
- **๐ Stability score** โ Composite 0โ100 score measuring consistency (p95, jitter, spikes, uptime)
|
|
84
|
-
- **๐
|
|
84
|
+
- **๐ Token usage tracking** โ The proxy logs prompt+completion token usage per exact provider/model pair, and the TUI surfaces that history in the `Used` column and the request log overlay.
|
|
85
85
|
- **๐ Request Log Overlay** โ Press `X` to inspect recent proxied requests and token usage for exact provider/model pairs.
|
|
86
86
|
- **๐ Changelog Overlay** โ Press `N` to browse all versions in an index, then `Enter` to view details for any version with full scroll support
|
|
87
87
|
- **๐ MODEL_NOT_FOUND Rotation** โ If a specific provider returns a 404 for a model, the TUI intelligently rotates through other available providers for the same model.
|
|
@@ -89,7 +89,7 @@ By Vanessa Depraute
|
|
|
89
89
|
- **๐ฎ Interactive selection** โ Navigate with arrow keys directly in the table, press Enter to act
|
|
90
90
|
- **๐ป OpenCode integration** โ Auto-detects NIM setup, sets model as default, launches OpenCode
|
|
91
91
|
- **๐ฆ OpenClaw integration** โ Sets selected model as default provider in `~/.openclaw/openclaw.json`
|
|
92
|
-
- **๐งฐ Public tool launchers** โ `Enter` auto-configures and launches
|
|
92
|
+
- **๐งฐ Public tool launchers** โ `Enter` auto-configures and launches all 13 tool modes: `OpenCode CLI`, `OpenCode Desktop`, `OpenClaw`, `Crush`, `Goose`, `Aider`, `Claude Code`, `Codex`, `Gemini`, `Qwen`, `OpenHands`, `Amp`, and `Pi`. All tools auto-select the chosen model on launch.
|
|
93
93
|
- **๐ Install Endpoints flow** โ Press `Y` to install one configured provider into the compatible persisted-config tools, with a choice between **Direct Provider** (pure API) or **FCM Proxy V2** (key rotation + usage tracking), then pick all models or a curated subset
|
|
94
94
|
- **๐ Feature Request (J key)** โ Send anonymous feedback directly to the project team
|
|
95
95
|
- **๐ Bug Report (I key)** โ Send anonymous bug reports directly to the project team
|
|
@@ -182,13 +182,12 @@ bunx free-coding-models YOUR_API_KEY
|
|
|
182
182
|
|
|
183
183
|
### ๐ What's New
|
|
184
184
|
|
|
185
|
-
**Version 0.3.
|
|
185
|
+
**Version 0.3.4 cleans up the public proxy/docs surface and ships a small stability pass:**
|
|
186
186
|
|
|
187
|
-
- **
|
|
188
|
-
-
|
|
189
|
-
- **
|
|
190
|
-
- **
|
|
191
|
-
- **Claude sync/install leftovers were removed from the proxy path** โ Claude Code is no longer treated like a persisted-config target for proxy sync; its integration is runtime-only, with fake Claude ids resolved by the proxy.
|
|
187
|
+
- **Browser hits on the proxy root are now friendly** โ `GET /` returns a small status JSON instead of `{"error":"Unauthorized"}` when you sanity-check the proxy in a browser.
|
|
188
|
+
- **`daemon stop` is now a real public CLI command** โ the help text, the README, and the command parser all agree on the same daemon control surface.
|
|
189
|
+
- **The README now matches the current UI exactly** โ model count is `160`, the `Used` column is documented correctly, and the removed `Usage` column is no longer described.
|
|
190
|
+
- **Malformed config sections are normalized safely on load** โ corrupted `apiKeys`, `providers`, or `settings` values no longer leak through as broken runtime objects.
|
|
192
191
|
|
|
193
192
|
---
|
|
194
193
|
|
|
@@ -246,7 +245,7 @@ Running `free-coding-models` with no launcher flag starts in **OpenCode CLI** mo
|
|
|
246
245
|
- The active target is always visible in the header badge before you press `Enter`
|
|
247
246
|
|
|
248
247
|
**How it works:**
|
|
249
|
-
1. **Ping phase** โ All enabled models are pinged in parallel (up to
|
|
248
|
+
1. **Ping phase** โ All enabled models are pinged in parallel (up to 160 across 20 providers)
|
|
250
249
|
2. **Continuous monitoring** โ Models start at 2s re-pings for 60s, then fall back to 10s automatically, and slow to 30s after 5 minutes idle unless you force 4s mode with `W`
|
|
251
250
|
3. **Real-time updates** โ Watch "Latest", "Avg", and "Up%" columns update live
|
|
252
251
|
4. **Select anytime** โ Use โโ arrows to navigate, press Enter on a model to act
|
|
@@ -429,7 +428,7 @@ TOGETHER_API_KEY=together_xxx free-coding-models
|
|
|
429
428
|
|
|
430
429
|
## ๐ค Coding Models
|
|
431
430
|
|
|
432
|
-
**
|
|
431
|
+
**160 coding models** across 20 providers and 8 tiers, ranked by [SWE-bench Verified](https://www.swebench.com) โ the industry-standard benchmark measuring real GitHub issue resolution. Scores are self-reported by providers unless noted.
|
|
433
432
|
|
|
434
433
|
### Alibaba Cloud (DashScope) (8 models)
|
|
435
434
|
|
|
@@ -513,7 +512,6 @@ The main table displays one row per model with the following columns:
|
|
|
513
512
|
| **Stability** | `B` | Composite 0โ100 consistency score (see [Stability Score](#-stability-score)) |
|
|
514
513
|
| **Up%** | `U` | Uptime โ percentage of successful pings |
|
|
515
514
|
| **Used** | โ | Total prompt+completion tokens consumed in logs for this exact provider/model pair, shown in `k` or `M` |
|
|
516
|
-
| **Usage** | `G` | Provider-scoped quota remaining when measurable; otherwise a green dot means usage % is not applicable/reliable for that provider |
|
|
517
515
|
|
|
518
516
|
### Verdict values
|
|
519
517
|
|
|
@@ -602,6 +600,8 @@ free-coding-models daemon uninstall # Remove OS service completely
|
|
|
602
600
|
free-coding-models daemon logs # Show recent service logs
|
|
603
601
|
```
|
|
604
602
|
|
|
603
|
+
For a quick browser sanity-check, open [http://127.0.0.1:18045/](http://127.0.0.1:18045/) or [http://127.0.0.1:18045/v1/health](http://127.0.0.1:18045/v1/health) while the proxy is running.
|
|
604
|
+
|
|
605
605
|
### Service management
|
|
606
606
|
|
|
607
607
|
The dedicated **FCM Proxy V2** overlay (accessible via `J` from main TUI, or Settings โ Enter) provides full control:
|
|
@@ -271,6 +271,18 @@ async function main() {
|
|
|
271
271
|
console.log()
|
|
272
272
|
process.exit(result.success ? 0 : 1)
|
|
273
273
|
}
|
|
274
|
+
if (daemonSubcmd === 'stop') {
|
|
275
|
+
const result = dm.stopDaemon()
|
|
276
|
+
console.log()
|
|
277
|
+
if (result.success) {
|
|
278
|
+
console.log(chalk.greenBright(' โ
FCM Proxy V2 service stopped.'))
|
|
279
|
+
console.log(chalk.dim(' The service stays installed and can be restarted later.'))
|
|
280
|
+
} else {
|
|
281
|
+
console.log(chalk.red(` โ Stop failed: ${result.error}`))
|
|
282
|
+
}
|
|
283
|
+
console.log()
|
|
284
|
+
process.exit(result.success ? 0 : 1)
|
|
285
|
+
}
|
|
274
286
|
if (daemonSubcmd === 'logs') {
|
|
275
287
|
const logPath = dm.getDaemonLogPath()
|
|
276
288
|
console.log(chalk.dim(` Log file: ${logPath}`))
|
|
@@ -283,7 +295,7 @@ async function main() {
|
|
|
283
295
|
process.exit(0)
|
|
284
296
|
}
|
|
285
297
|
console.log(chalk.red(` Unknown command: ${daemonSubcmd}`))
|
|
286
|
-
console.log(chalk.dim(' Usage: free-coding-models daemon [status|install|uninstall|restart|logs]'))
|
|
298
|
+
console.log(chalk.dim(' Usage: free-coding-models daemon [status|install|uninstall|restart|stop|logs]'))
|
|
287
299
|
process.exit(1)
|
|
288
300
|
}
|
|
289
301
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "free-coding-models",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.4",
|
|
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",
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* and OpenAI Chat Completions API.
|
|
5
5
|
*
|
|
6
6
|
* ๐ This is the key module that enables Claude Code to work natively through the
|
|
7
|
-
* FCM proxy without needing the external
|
|
7
|
+
* FCM proxy without needing the external Claude proxy integration.
|
|
8
8
|
* Claude Code sends requests in Anthropic format (POST /v1/messages) and this
|
|
9
9
|
* module translates them to OpenAI format for the upstream providers, then
|
|
10
10
|
* translates the responses back.
|
package/src/cli-help.js
CHANGED
|
@@ -40,6 +40,7 @@ const COMMANDS = [
|
|
|
40
40
|
{ command: 'daemon install', description: 'Install and start the background service' },
|
|
41
41
|
{ command: 'daemon uninstall', description: 'Remove the background service' },
|
|
42
42
|
{ command: 'daemon restart', description: 'Restart the background service' },
|
|
43
|
+
{ command: 'daemon stop', description: 'Gracefully stop the background service without uninstalling it' },
|
|
43
44
|
{ command: 'daemon logs', description: 'Print the latest daemon log lines' },
|
|
44
45
|
]
|
|
45
46
|
|
|
@@ -70,7 +71,7 @@ export function buildCliHelpLines({ chalk = null, indent = '', title = 'CLI Help
|
|
|
70
71
|
|
|
71
72
|
lines.push(`${indent}${paint(chalk, chalk?.bold, title)}`)
|
|
72
73
|
lines.push(`${indent}${paint(chalk, chalk?.dim, 'Usage: free-coding-models [apiKey] [options]')}`)
|
|
73
|
-
lines.push(`${indent}${paint(chalk, chalk?.dim, ' free-coding-models daemon [status|install|uninstall|restart|logs]')}`)
|
|
74
|
+
lines.push(`${indent}${paint(chalk, chalk?.dim, ' free-coding-models daemon [status|install|uninstall|restart|stop|logs]')}`)
|
|
74
75
|
lines.push('')
|
|
75
76
|
lines.push(`${indent}${paint(chalk, chalk?.bold, 'Tool Flags')}`)
|
|
76
77
|
for (const entry of launchFlags) {
|
package/src/config.js
CHANGED
|
@@ -183,10 +183,10 @@ export function loadConfig() {
|
|
|
183
183
|
try {
|
|
184
184
|
const raw = readFileSync(CONFIG_PATH, 'utf8').trim()
|
|
185
185
|
const parsed = JSON.parse(raw)
|
|
186
|
-
// ๐ Ensure the shape is always complete โ fill missing sections with defaults
|
|
187
|
-
if (!parsed.apiKeys) parsed.apiKeys = {}
|
|
188
|
-
if (!parsed.providers) parsed.providers = {}
|
|
189
|
-
if (!parsed.settings || typeof parsed.settings !== 'object') parsed.settings = {}
|
|
186
|
+
// ๐ Ensure the shape is always complete โ fill missing or corrupted sections with defaults.
|
|
187
|
+
if (!parsed.apiKeys || typeof parsed.apiKeys !== 'object' || Array.isArray(parsed.apiKeys)) parsed.apiKeys = {}
|
|
188
|
+
if (!parsed.providers || typeof parsed.providers !== 'object' || Array.isArray(parsed.providers)) parsed.providers = {}
|
|
189
|
+
if (!parsed.settings || typeof parsed.settings !== 'object' || Array.isArray(parsed.settings)) parsed.settings = {}
|
|
190
190
|
if (typeof parsed.settings.hideUnconfiguredModels !== 'boolean') parsed.settings.hideUnconfiguredModels = true
|
|
191
191
|
parsed.settings.proxy = normalizeProxySettings(parsed.settings.proxy)
|
|
192
192
|
// ๐ Favorites: list of "providerKey/modelId" pinned rows.
|
|
@@ -443,9 +443,11 @@ export function validateConfigFile(options = {}) {
|
|
|
443
443
|
throw new Error('Config is not a valid object')
|
|
444
444
|
}
|
|
445
445
|
|
|
446
|
-
// ๐ Check for critical corruption (apiKeys should be an object if it exists)
|
|
447
|
-
|
|
448
|
-
|
|
446
|
+
// ๐ Check for critical corruption (apiKeys should be an object if it exists).
|
|
447
|
+
// ๐ Treat this as recoverable โ loadConfig() will normalize the value safely.
|
|
448
|
+
if (parsed.apiKeys !== null && parsed.apiKeys !== undefined
|
|
449
|
+
&& (typeof parsed.apiKeys !== 'object' || Array.isArray(parsed.apiKeys))) {
|
|
450
|
+
console.warn('โ ๏ธ apiKeys field malformed; it will be normalized on load')
|
|
449
451
|
}
|
|
450
452
|
|
|
451
453
|
return { valid: true }
|
package/src/opencode.js
CHANGED
|
@@ -577,7 +577,7 @@ export async function ensureProxyRunning(fcmConfig, { forceRestart = false } = {
|
|
|
577
577
|
|
|
578
578
|
// ๐ Always prefer the background daemon when it is available. Launcher code
|
|
579
579
|
// ๐ can update config and let the daemon hot-reload, which is closer to the
|
|
580
|
-
// ๐
|
|
580
|
+
// ๐ Claude proxy model than spinning up tool-specific local proxies.
|
|
581
581
|
try {
|
|
582
582
|
const daemonRunning = await isDaemonRunning()
|
|
583
583
|
if (daemonRunning) {
|
package/src/proxy-server.js
CHANGED
|
@@ -303,6 +303,12 @@ export class ProxyServer {
|
|
|
303
303
|
// โโ Request routing โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
304
304
|
|
|
305
305
|
_handleRequest(req, res) {
|
|
306
|
+
// ๐ Root endpoint is unauthenticated so a browser hit on http://127.0.0.1:{port}/
|
|
307
|
+
// ๐ gives a useful status payload instead of a misleading Unauthorized error.
|
|
308
|
+
if (req.method === 'GET' && req.url === '/') {
|
|
309
|
+
return this._handleRoot(res)
|
|
310
|
+
}
|
|
311
|
+
|
|
306
312
|
// ๐ Health endpoint is unauthenticated so external monitors can probe it
|
|
307
313
|
if (req.method === 'GET' && req.url === '/v1/health') {
|
|
308
314
|
return this._handleHealth(res)
|
|
@@ -780,6 +786,26 @@ export class ProxyServer {
|
|
|
780
786
|
|
|
781
787
|
// โโ GET /v1/health โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
782
788
|
|
|
789
|
+
/**
|
|
790
|
+
* ๐ Friendly unauthenticated landing endpoint for browsers and quick local checks.
|
|
791
|
+
*/
|
|
792
|
+
_handleRoot(res) {
|
|
793
|
+
const status = this.getStatus()
|
|
794
|
+
const uniqueModels = new Set(this._accounts.map(acct => acct.proxyModelId || acct.modelId)).size
|
|
795
|
+
sendJson(res, 200, {
|
|
796
|
+
status: 'ok',
|
|
797
|
+
service: 'fcm-proxy-v2',
|
|
798
|
+
running: status.running,
|
|
799
|
+
accountCount: status.accountCount,
|
|
800
|
+
modelCount: uniqueModels,
|
|
801
|
+
endpoints: {
|
|
802
|
+
health: '/v1/health',
|
|
803
|
+
models: '/v1/models',
|
|
804
|
+
stats: '/v1/stats',
|
|
805
|
+
},
|
|
806
|
+
})
|
|
807
|
+
}
|
|
808
|
+
|
|
783
809
|
/**
|
|
784
810
|
* ๐ Health endpoint for daemon liveness checks. Unauthenticated so external
|
|
785
811
|
* monitors (TUI, launchctl, systemd) can probe without needing the token.
|
package/src/proxy-sync.js
CHANGED
|
@@ -34,7 +34,7 @@ const PROXY_PROVIDER_ID = 'fcm-proxy'
|
|
|
34
34
|
|
|
35
35
|
// ๐ Tools that support proxy sync (have base URL + API key config)
|
|
36
36
|
// ๐ Gemini is excluded โ it only stores a model name, no URL/key fields.
|
|
37
|
-
// ๐ Claude
|
|
37
|
+
// ๐ Claude proxy integration is
|
|
38
38
|
// ๐ runtime-only now, with fake Claude ids handled by the proxy itself.
|
|
39
39
|
export const PROXY_SYNCABLE_TOOLS = [
|
|
40
40
|
'opencode', 'opencode-desktop', 'openclaw', 'crush', 'goose', 'pi',
|
package/src/proxy-topology.js
CHANGED
|
@@ -67,7 +67,7 @@ export function buildProxyTopologyFromConfig(fcmConfig, mergedModels, sourcesMap
|
|
|
67
67
|
return {
|
|
68
68
|
accounts,
|
|
69
69
|
proxyModels,
|
|
70
|
-
// ๐ Mirror
|
|
70
|
+
// ๐ Mirror Claude proxy: proxy-side Claude family routing is config-driven.
|
|
71
71
|
anthropicRouting: getProxySettings(fcmConfig).anthropicRouting,
|
|
72
72
|
}
|
|
73
73
|
}
|
package/src/tool-launchers.js
CHANGED
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
* ๐ Crush: writes crush.json with provider config + models.large/small defaults
|
|
20
20
|
* ๐ Pi: uses --provider/--model CLI flags for guaranteed auto-selection
|
|
21
21
|
* ๐ Aider: writes ~/.aider.conf.yml + passes --model flag
|
|
22
|
-
* ๐ Claude Code: mirrors
|
|
22
|
+
* ๐ Claude Code: mirrors Claude proxy by keeping fake Claude model ids on the client,
|
|
23
23
|
* forcing a valid Claude alias at launch, and moving MODEL / MODEL_OPUS / MODEL_SONNET /
|
|
24
24
|
* MODEL_HAIKU routing into the proxy
|
|
25
25
|
* ๐ Codex CLI: uses a custom model_provider override so Codex stays in explicit API-provider mode
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
*
|
|
28
28
|
* @functions
|
|
29
29
|
* โ `resolveLauncherModelId` โ choose the provider-specific id or proxy slug for a launch
|
|
30
|
-
* โ `waitForClaudeProxyRouting` โ wait until the daemon/proxy has reloaded the
|
|
30
|
+
* โ `waitForClaudeProxyRouting` โ wait until the daemon/proxy has reloaded the Claude proxy style Claude-family mapping
|
|
31
31
|
* โ `buildClaudeProxyArgs` โ force a valid Claude alias so stale local non-Claude selections cannot break launch
|
|
32
32
|
* โ `buildCodexProxyArgs` โ force Codex into a proxy-backed custom provider config
|
|
33
33
|
* โ `inspectGeminiCliSupport` โ detect whether the installed Gemini CLI can use proxy mode safely
|
|
@@ -646,7 +646,7 @@ export async function startExternalTool(mode, model, config) {
|
|
|
646
646
|
}
|
|
647
647
|
|
|
648
648
|
if (mode === 'claude-code') {
|
|
649
|
-
// ๐ Mirror
|
|
649
|
+
// ๐ Mirror Claude proxy exactly on the client side:
|
|
650
650
|
// ๐ Claude gets only ANTHROPIC_BASE_URL + ANTHROPIC_AUTH_TOKEN, and the
|
|
651
651
|
// ๐ proxy owns the fake Claude model ids -> real backend model mapping.
|
|
652
652
|
const launchModelId = resolveLauncherModelId(model, true)
|