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 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-159-76b900?logo=nvidia" alt="models count">
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
- - **๐Ÿ“Š Usage tracking** โ€” Monitor remaining quota for each exact provider/model pair when the provider exposes it; otherwise the TUI shows a green dot instead of a misleading percentage.
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 10+ tools: `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.
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.3 switches Claude Code to the exact `free-claude-code` pattern instead of injecting FCM slugs into Claude itself:**
185
+ **Version 0.3.4 cleans up the public proxy/docs surface and ships a small stability pass:**
186
186
 
187
- - **Claude Code is proxy-only now** โ€” FCM no longer tries to make Claude Code โ€œselectโ€ `gpt-oss-120b` or any other free model directly. Claude still speaks in Claude model ids, and the proxy picks the real backend.
188
- - **Proxy-side `MODEL` / `MODEL_OPUS` / `MODEL_SONNET` / `MODEL_HAIKU` routing now drives Claude** โ€” When you launch Claude Code from a selected FCM row, FCM writes the selected proxy slug into the proxy's Anthropic routing config, exactly like `free-claude-code`, then lets the daemon hot-reload it.
189
- - **Claude no longer gets `--model <fcm-slug>` or `ANTHROPIC_MODEL=<fcm-slug>`** โ€” the launcher now passes only `ANTHROPIC_BASE_URL` and `ANTHROPIC_AUTH_TOKEN`, which is the same clean client-side contract used by `free-claude-code`.
190
- - **Claude is now forced onto a real Claude alias at launch** โ€” FCM starts Claude Code with `--model sonnet`, so stale local values like `gpt-oss-120b` cannot fail client-side before the proxy even receives the request.
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 159 across 20 providers)
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
- **159 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.
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",
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 "free-claude-code" Python proxy.
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
- if (parsed.apiKeys !== null && parsed.apiKeys !== undefined && typeof parsed.apiKeys !== 'object') {
448
- throw new Error('apiKeys field is corrupted')
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
- // ๐Ÿ“– free-claude-code model than spinning up tool-specific local proxies.
580
+ // ๐Ÿ“– Claude proxy model than spinning up tool-specific local proxies.
581
581
  try {
582
582
  const daemonRunning = await isDaemonRunning()
583
583
  if (daemonRunning) {
@@ -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 Code is excluded too: its free-claude-code style integration is
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',
@@ -67,7 +67,7 @@ export function buildProxyTopologyFromConfig(fcmConfig, mergedModels, sourcesMap
67
67
  return {
68
68
  accounts,
69
69
  proxyModels,
70
- // ๐Ÿ“– Mirror free-claude-code: proxy-side Claude family routing is config-driven.
70
+ // ๐Ÿ“– Mirror Claude proxy: proxy-side Claude family routing is config-driven.
71
71
  anthropicRouting: getProxySettings(fcmConfig).anthropicRouting,
72
72
  }
73
73
  }
@@ -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 free-claude-code by keeping fake Claude model ids on the client,
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 free-claude-code style Claude-family mapping
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 free-claude-code exactly on the client side:
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)