free-coding-models 0.3.6 → 0.3.11
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 +32 -0
- package/README.md +43 -0
- package/bin/free-coding-models.js +75 -33
- package/package.json +1 -1
- package/src/cli-help.js +9 -1
- package/src/config.js +18 -240
- package/src/error-classifier.js +4 -1
- package/src/favorites.js +0 -14
- package/src/key-handler.js +26 -212
- package/src/overlays.js +10 -37
- package/src/proxy-foreground.js +234 -0
- package/src/proxy-server.js +41 -12
- package/src/proxy-sync.js +28 -2
- package/src/render-table.js +6 -17
- package/src/token-usage-reader.js +53 -11
- package/src/utils.js +44 -9
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,38 @@
|
|
|
2
2
|
|
|
3
3
|
---
|
|
4
4
|
|
|
5
|
+
## 0.3.11
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
- Added early 404 response when a requested model has no registered accounts, ensuring clear error handling.
|
|
9
|
+
|
|
10
|
+
### Removed
|
|
11
|
+
- **Profile system**: Entire profile system removed to ensure API keys persist permanently across all sessions. No more profile switching causing API key loss.
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
- **`--proxy` foreground mode**: New `--proxy` flag starts FCM Proxy V2 in the current terminal with a live dashboard showing status, accounts, provider breakdown, and real-time request log. No daemon install needed — works from dev checkout too.
|
|
15
|
+
|
|
16
|
+
### Fixed
|
|
17
|
+
- **Claude Code proxy auth**: Proxy now accepts `x-api-key` header (used by Anthropic SDK / Claude Code) in addition to `Authorization: Bearer`
|
|
18
|
+
- **Claude model fallback**: When `anthropicRouting` is all null, Claude model names now fall back to the first available account instead of returning "Model not found"
|
|
19
|
+
- **Proxy sync for Claude Code**: Added `claude-code` to `PROXY_SYNCABLE_TOOLS` so the env file is properly written/updated by proxy sync
|
|
20
|
+
- **Correct env var name**: Claude Code env file now exports `ANTHROPIC_API_KEY` (SDK standard) instead of `ANTHROPIC_AUTH_TOKEN`
|
|
21
|
+
- **Auto-source shell profile**: Claude Code env file is now automatically sourced in `.zshrc` / `.bashrc` / `.bash_profile`
|
|
22
|
+
- **Removed deleted profile functions from tests**: Cleaned up test imports after profile system removal from config.js
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## 0.3.9
|
|
27
|
+
|
|
28
|
+
### Improved
|
|
29
|
+
- **Enhanced `--premium` flag**: Now applies strict elite-only constraints. Shows only **S/S+** tier models with perfect health (**UP**) and a good verdict (**Perfect**, **Normal**, or **Slow**). Models with 429 errors, auth failures, or poor performance are automatically hidden.
|
|
30
|
+
- **Accurate Token Usage Tracking**: The "Used" column now uses the persistent `token-stats.json` file as the source of truth, providing accurate historical totals instead of only the most recent logs.
|
|
31
|
+
- **Enhanced Log Transparency**: The request log page now always shows the requested model and the actual upstream model (e.g., `llama-3.1-405b → meta/llama-3.1-405b-instruct`) whenever they differ.
|
|
32
|
+
- **Pretty Provider Labels**: The request log page now uses human-readable provider labels (e.g., "NVIDIA NIM", "SambaNova") instead of raw internal keys.
|
|
33
|
+
- **Fixed Tier Filtering Family Logic**: Updated `--tier S` behavior to correctly include both **S** and **S+** models (matching documentation).
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
5
37
|
## 0.3.6
|
|
6
38
|
|
|
7
39
|
### Added
|
package/README.md
CHANGED
|
@@ -200,6 +200,30 @@ free-coding-models
|
|
|
200
200
|
# Explicitly target OpenCode CLI (TUI + Enter launches OpenCode CLI)
|
|
201
201
|
free-coding-models --opencode
|
|
202
202
|
|
|
203
|
+
## 📋 CLI Flags (expanded)
|
|
204
|
+
|
|
205
|
+
The tool now supports a comprehensive set of flags to fine‑tune its behavior. All flags can be combined in any order.
|
|
206
|
+
|
|
207
|
+
| Flag | Type | Description |
|
|
208
|
+
|------|------|-------------|
|
|
209
|
+
| `--best` | boolean | Show only top‑tier models (A+, S, S+). |
|
|
210
|
+
| `--fiable` | boolean | Run a 10 s reliability analysis and output the most reliable model. |
|
|
211
|
+
| `--json` | boolean | Output results as JSON for scripting/automation. |
|
|
212
|
+
| `--tier <S|A|B|C>` | value | Filter models by tier family (e.g. `S` shows S+ and S). |
|
|
213
|
+
| `--recommend` | boolean | Open Smart Recommend mode immediately on startup. |
|
|
214
|
+
| `--sort <column>` | value | Sort by a specific column (`rank`, `tier`, `origin`, `model`, `ping`, `avg`, `swe`, `ctx`, `condition`, `verdict`, `uptime`, `stability`, `usage`). |
|
|
215
|
+
| `--desc` / `--asc` | boolean | Set sort direction explicitly (descending or ascending). |
|
|
216
|
+
| `--origin <provider>` | value | Filter models by provider origin (e.g. `nvidia`, `groq`). |
|
|
217
|
+
| `--ping-interval <ms>` | value | Override the ping interval in milliseconds (affects live monitoring speed). |
|
|
218
|
+
| `--hide-unconfigured` | boolean | Hide models whose providers have no configured API key. |
|
|
219
|
+
| `--show-unconfigured` | boolean | Show all models regardless of API key configuration. |
|
|
220
|
+
| `--disable-widths-warning` | boolean | Disable the terminal width warning banner. |
|
|
221
|
+
| `--profile <name>` | value | Load a saved configuration profile before startup. |
|
|
222
|
+
| `--no-telemetry` | boolean | Disable anonymous telemetry for this run. |
|
|
223
|
+
| `--clean-proxy`, `--proxy-clean` | boolean | Remove persisted FCM proxy configuration from OpenCode. |
|
|
224
|
+
| `--help`, `-h` | boolean | Print the complete help text and exit. |
|
|
225
|
+
|
|
226
|
+
These flags are also reflected in the built‑in help (`free-coding-models --help`).
|
|
203
227
|
# Explicitly target OpenCode Desktop (TUI + Enter sets model & opens Desktop app)
|
|
204
228
|
free-coding-models --opencode-desktop
|
|
205
229
|
|
|
@@ -920,6 +944,25 @@ This script:
|
|
|
920
944
|
|
|
921
945
|
## 📋 API Reference
|
|
922
946
|
|
|
947
|
+
### 🎁 Premium Flag
|
|
948
|
+
|
|
949
|
+
The `--premium` flag provides a quick view of only the elite **S/S+ tier** models with perfect health (**UP**) and a good verdict (**Perfect**, **Normal**, or **Slow**). This is useful when you want to focus exclusively on the highest‑quality, most reliable models that are currently available.
|
|
950
|
+
|
|
951
|
+
```bash
|
|
952
|
+
free-coding-models --premium
|
|
953
|
+
```
|
|
954
|
+
|
|
955
|
+
What it does under the hood:
|
|
956
|
+
- Sets `tierFilter` to `S` (showing only S+ and S tier models).
|
|
957
|
+
- Filters out any model that is not currently **UP** (hides 429, 410, auth fail, timeouts, etc.).
|
|
958
|
+
- Filters out models with poor verdicts (hides **Spiky**, **Very Slow**, **Overloaded**, **Unstable**, etc.).
|
|
959
|
+
- Forces the sort column to `verdict` with ascending order, so the best‑rated models appear at the top.
|
|
960
|
+
- Leaves other settings untouched, so you can still combine it with flags like `--json` for scripting.
|
|
961
|
+
|
|
962
|
+
You can combine `--premium` with other flags (e.g., `--json --hide-unconfigured`) to further tailor the output.
|
|
963
|
+
|
|
964
|
+
---
|
|
965
|
+
|
|
923
966
|
**Environment variables (override config file):**
|
|
924
967
|
|
|
925
968
|
| Variable | Description |
|
|
@@ -99,7 +99,7 @@ import { homedir } from 'os'
|
|
|
99
99
|
import { join, dirname } from 'path'
|
|
100
100
|
import { MODELS, sources } from '../sources.js'
|
|
101
101
|
import { getAvg, getVerdict, getUptime, getP95, getJitter, getStabilityScore, sortResults, filterByTier, findBestModel, parseArgs, TIER_ORDER, VERDICT_ORDER, TIER_LETTER_MAP, scoreModelForTask, getTopRecommendations, TASK_TYPES, PRIORITY_TYPES, CONTEXT_BUDGETS, formatCtxWindow, labelFromId, getProxyStatusInfo, formatResultsAsJSON } from '../src/utils.js'
|
|
102
|
-
import { loadConfig, saveConfig, getApiKey, getProxySettings, resolveApiKeys, addApiKey, removeApiKey, isProviderEnabled,
|
|
102
|
+
import { loadConfig, saveConfig, getApiKey, getProxySettings, resolveApiKeys, addApiKey, removeApiKey, isProviderEnabled, persistApiKeysForProvider } from '../src/config.js'
|
|
103
103
|
import { buildMergedModels } from '../src/model-merger.js'
|
|
104
104
|
import { ProxyServer } from '../src/proxy-server.js'
|
|
105
105
|
import { loadOpenCodeConfig, saveOpenCodeConfig, syncToOpenCode, restoreOpenCodeBackup, cleanupOpenCodeProxyConfig } from '../src/opencode-sync.js'
|
|
@@ -126,6 +126,7 @@ import { createOverlayRenderers } from '../src/overlays.js'
|
|
|
126
126
|
import { createKeyHandler } from '../src/key-handler.js'
|
|
127
127
|
import { getToolModeOrder, getToolMeta } from '../src/tool-metadata.js'
|
|
128
128
|
import { startExternalTool } from '../src/tool-launchers.js'
|
|
129
|
+
import { startForegroundProxy } from '../src/proxy-foreground.js'
|
|
129
130
|
import { getConfiguredInstallableProviders, installProviderEndpoints, refreshInstalledEndpoints, getInstallTargetModes, getProviderCatalogModels, CONNECTION_MODES } from '../src/endpoint-installer.js'
|
|
130
131
|
import { loadCache, saveCache, clearCache, getCacheAge } from '../src/cache.js'
|
|
131
132
|
import { checkConfigSecurity } from '../src/security.js'
|
|
@@ -203,6 +204,22 @@ async function main() {
|
|
|
203
204
|
// 📖 User declined auto-fix or it failed — continue anyway, just warned
|
|
204
205
|
}
|
|
205
206
|
|
|
207
|
+
// 📖 Apply CLI overrides for settings
|
|
208
|
+
if (cliArgs.sortColumn) config.settings.sortColumn = cliArgs.sortColumn
|
|
209
|
+
if (cliArgs.sortDirection) config.settings.sortAsc = cliArgs.sortDirection === 'asc'
|
|
210
|
+
if (cliArgs.originFilter) config.settings.originFilter = cliArgs.originFilter
|
|
211
|
+
if (cliArgs.pingInterval) config.settings.pingInterval = cliArgs.pingInterval
|
|
212
|
+
if (cliArgs.hideUnconfigured) config.settings.hideUnconfiguredModels = true
|
|
213
|
+
if (cliArgs.showUnconfigured) config.settings.hideUnconfiguredModels = false
|
|
214
|
+
if (cliArgs.disableWidthsWarning) config.settings.disableWidthsWarning = true
|
|
215
|
+
|
|
216
|
+
// 📖 Apply premium mode: show only S‑tier models sorted by verdict
|
|
217
|
+
if (cliArgs.premiumMode) {
|
|
218
|
+
config.settings.tierFilter = 'S'
|
|
219
|
+
config.settings.sortColumn = 'verdict'
|
|
220
|
+
config.settings.sortAsc = true
|
|
221
|
+
}
|
|
222
|
+
|
|
206
223
|
if (cliArgs.cleanProxyMode) {
|
|
207
224
|
const cleaned = cleanupOpenCodeProxyConfig()
|
|
208
225
|
console.log()
|
|
@@ -213,6 +230,12 @@ async function main() {
|
|
|
213
230
|
process.exit(0)
|
|
214
231
|
}
|
|
215
232
|
|
|
233
|
+
// 📖 Foreground proxy mode — starts the proxy in the current terminal with live dashboard
|
|
234
|
+
if (cliArgs.proxyForegroundMode) {
|
|
235
|
+
await startForegroundProxy(config, chalk)
|
|
236
|
+
return // 📖 startForegroundProxy keeps the process alive via signal handlers
|
|
237
|
+
}
|
|
238
|
+
|
|
216
239
|
// 📖 CLI subcommand: free-coding-models daemon <action>
|
|
217
240
|
const daemonSubcmd = process.argv[2] === 'daemon' ? (process.argv[3] || 'status') : null
|
|
218
241
|
if (daemonSubcmd) {
|
|
@@ -299,19 +322,7 @@ async function main() {
|
|
|
299
322
|
process.exit(1)
|
|
300
323
|
}
|
|
301
324
|
|
|
302
|
-
// 📖
|
|
303
|
-
let startupProfileSettings = null
|
|
304
|
-
if (cliArgs.profileName) {
|
|
305
|
-
startupProfileSettings = loadProfile(config, cliArgs.profileName)
|
|
306
|
-
if (!startupProfileSettings) {
|
|
307
|
-
console.error(chalk.red(` Unknown profile "${cliArgs.profileName}". Available: ${listProfiles(config).join(', ') || '(none)'}`))
|
|
308
|
-
process.exit(1)
|
|
309
|
-
}
|
|
310
|
-
saveConfig(config, {
|
|
311
|
-
replaceApiKeys: true,
|
|
312
|
-
replaceFavorites: true,
|
|
313
|
-
})
|
|
314
|
-
}
|
|
325
|
+
// 📖 Profile system removed - API keys now persist permanently across all sessions
|
|
315
326
|
|
|
316
327
|
// 📖 Check if any provider has a key — if not, run the first-time setup wizard
|
|
317
328
|
const hasAnyKey = Object.keys(sources).some(pk => !!getApiKey(config, pk))
|
|
@@ -478,15 +489,15 @@ async function main() {
|
|
|
478
489
|
return 'normal'
|
|
479
490
|
}
|
|
480
491
|
|
|
481
|
-
|
|
492
|
+
// 📖 tierFilter: current tier filter letter (null = all, 'S' = S+/S, 'A' = A+/A/A-, etc.)
|
|
482
493
|
const state = {
|
|
483
494
|
results,
|
|
484
495
|
pendingPings: 0,
|
|
485
496
|
frame: 0,
|
|
486
497
|
cursor: 0,
|
|
487
498
|
selectedModel: null,
|
|
488
|
-
sortColumn:
|
|
489
|
-
sortDirection: (
|
|
499
|
+
sortColumn: config.settings?.sortColumn ?? 'avg',
|
|
500
|
+
sortDirection: (config.settings?.sortAsc ?? true) ? 'asc' : 'desc',
|
|
490
501
|
pingInterval: PING_MODE_INTERVALS.speed, // 📖 Effective live interval derived from the active ping mode.
|
|
491
502
|
pingMode: 'speed', // 📖 Current ping mode: speed | normal | slow | forced.
|
|
492
503
|
pingModeSource: 'startup', // 📖 Why this mode is active: startup | manual | auto | idle | activity.
|
|
@@ -499,8 +510,9 @@ async function main() {
|
|
|
499
510
|
mode, // 📖 'opencode' or 'openclaw' — controls Enter action
|
|
500
511
|
tierFilterMode: 0, // 📖 Index into TIER_CYCLE (0=All, 1=S+, 2=S, ...)
|
|
501
512
|
originFilterMode: 0, // 📖 Index into ORIGIN_CYCLE (0=All, then providers)
|
|
502
|
-
|
|
503
|
-
|
|
513
|
+
premiumMode: cliArgs.premiumMode, // 📖 Special elite-only mode: S/S+ only, Health UP only, Perfect/Normal/Slow verdict only.
|
|
514
|
+
hideUnconfiguredModels: config.settings?.hideUnconfiguredModels === true, // 📖 Hide providers with no configured API key when true.
|
|
515
|
+
disableWidthsWarning: config.settings?.disableWidthsWarning ?? false, // 📖 Disable widths warning toggle (default off)
|
|
504
516
|
scrollOffset: 0, // 📖 First visible model index in viewport
|
|
505
517
|
terminalRows: process.stdout.rows || 24, // 📖 Current terminal height
|
|
506
518
|
terminalCols: process.stdout.columns || 80, // 📖 Current terminal width
|
|
@@ -557,10 +569,6 @@ hideUnconfiguredModels: startupProfileSettings?.hideUnconfiguredModels === true
|
|
|
557
569
|
recommendAnalysisTimer: null, // 📖 setInterval handle for the 10s analysis phase
|
|
558
570
|
recommendPingTimer: null, // 📖 setInterval handle for 2 pings/sec during analysis
|
|
559
571
|
recommendedKeys: new Set(), // 📖 Set of "providerKey/modelId" for recommended models (shown in main table)
|
|
560
|
-
// 📖 Config Profiles state
|
|
561
|
-
activeProfile: getActiveProfileName(config), // 📖 Currently loaded profile name (or null)
|
|
562
|
-
profileSaveMode: false, // 📖 Whether the inline "Save profile" name input is active
|
|
563
|
-
profileSaveBuffer: '', // 📖 Typed characters for the profile name being saved
|
|
564
572
|
// 📖 Feedback state (J/I keys open it)
|
|
565
573
|
feedbackOpen: false, // 📖 Whether the feedback overlay is active
|
|
566
574
|
bugReportBuffer: '', // 📖 Typed characters for the feedback message
|
|
@@ -756,6 +764,17 @@ hideUnconfiguredModels: startupProfileSettings?.hideUnconfiguredModels === true
|
|
|
756
764
|
outputResults = outputResults.filter(r => ['S+', 'S', 'A+'].includes(r.tier))
|
|
757
765
|
}
|
|
758
766
|
|
|
767
|
+
// 📖 Apply premium mode filter if specified: elite-only (S/S+, UP, Good Verdict)
|
|
768
|
+
if (cliArgs.premiumMode) {
|
|
769
|
+
outputResults = outputResults.filter(r => {
|
|
770
|
+
const isEliteTier = r.tier === 'S' || r.tier === 'S+'
|
|
771
|
+
const isHealthUp = r.status === 'up'
|
|
772
|
+
const verdict = getVerdict(r)
|
|
773
|
+
const isGoodVerdict = ['Perfect', 'Normal', 'Slow'].includes(verdict)
|
|
774
|
+
return isEliteTier && isHealthUp && isGoodVerdict
|
|
775
|
+
})
|
|
776
|
+
}
|
|
777
|
+
|
|
759
778
|
// 📖 Sort by avg ping (ascending)
|
|
760
779
|
outputResults = sortResults(outputResults, 'avg', 'asc')
|
|
761
780
|
|
|
@@ -770,6 +789,9 @@ hideUnconfiguredModels: startupProfileSettings?.hideUnconfiguredModels === true
|
|
|
770
789
|
|
|
771
790
|
// 📖 Enter alternate screen — animation runs here, zero scrollback pollution
|
|
772
791
|
process.stdout.write(ALT_ENTER)
|
|
792
|
+
if (process.stdout.isTTY) {
|
|
793
|
+
process.stdout.flush && process.stdout.flush()
|
|
794
|
+
}
|
|
773
795
|
|
|
774
796
|
// 📖 Ensure we always leave alt screen cleanly (Ctrl+C, crash, normal exit)
|
|
775
797
|
const exit = (code = 0) => {
|
|
@@ -778,6 +800,9 @@ hideUnconfiguredModels: startupProfileSettings?.hideUnconfiguredModels === true
|
|
|
778
800
|
clearInterval(ticker)
|
|
779
801
|
clearTimeout(state.pingIntervalObj)
|
|
780
802
|
process.stdout.write(ALT_LEAVE)
|
|
803
|
+
if (process.stdout.isTTY) {
|
|
804
|
+
process.stdout.flush && process.stdout.flush()
|
|
805
|
+
}
|
|
781
806
|
process.exit(code)
|
|
782
807
|
}
|
|
783
808
|
process.on('SIGINT', () => exit(0))
|
|
@@ -785,7 +810,7 @@ hideUnconfiguredModels: startupProfileSettings?.hideUnconfiguredModels === true
|
|
|
785
810
|
|
|
786
811
|
// 📖 originFilterMode: index into ORIGIN_CYCLE, 0=All, then each provider key in order
|
|
787
812
|
const ORIGIN_CYCLE = [null, ...Object.keys(sources)]
|
|
788
|
-
const resolvedTierFilter =
|
|
813
|
+
const resolvedTierFilter = config.settings?.tierFilter
|
|
789
814
|
state.tierFilterMode = resolvedTierFilter ? Math.max(0, TIER_CYCLE.indexOf(resolvedTierFilter)) : 0
|
|
790
815
|
const resolvedOriginFilter = config.settings?.originFilter
|
|
791
816
|
state.originFilterMode = resolvedOriginFilter ? Math.max(0, ORIGIN_CYCLE.indexOf(resolvedOriginFilter)) : 0
|
|
@@ -805,9 +830,23 @@ hideUnconfiguredModels: startupProfileSettings?.hideUnconfiguredModels === true
|
|
|
805
830
|
return
|
|
806
831
|
}
|
|
807
832
|
// 📖 Apply both tier and origin filters — model is hidden if it fails either
|
|
808
|
-
|
|
833
|
+
// 📖 TIER_LETTER_MAP is used so --tier S also includes S+ models (tier family behavior).
|
|
834
|
+
const allowedTiers = (activeTier && TIER_LETTER_MAP[activeTier]) ? TIER_LETTER_MAP[activeTier] : [activeTier]
|
|
835
|
+
const tierHide = activeTier !== null && !allowedTiers.includes(r.tier)
|
|
809
836
|
const originHide = activeOrigin !== null && r.providerKey !== activeOrigin
|
|
810
837
|
r.hidden = tierHide || originHide
|
|
838
|
+
|
|
839
|
+
// 📖 Premium Mode: elite-only constraints (Health UP, Good Verdict, S/S+ only)
|
|
840
|
+
if (state.premiumMode && !r.hidden) {
|
|
841
|
+
const isEliteTier = r.tier === 'S' || r.tier === 'S+'
|
|
842
|
+
const isHealthUp = r.status === 'up'
|
|
843
|
+
const verdict = getVerdict(r)
|
|
844
|
+
const isGoodVerdict = ['Perfect', 'Normal', 'Slow'].includes(verdict)
|
|
845
|
+
|
|
846
|
+
if (!isEliteTier || !isHealthUp || !isGoodVerdict) {
|
|
847
|
+
r.hidden = true
|
|
848
|
+
}
|
|
849
|
+
}
|
|
811
850
|
})
|
|
812
851
|
return state.results
|
|
813
852
|
}
|
|
@@ -823,6 +862,9 @@ hideUnconfiguredModels: startupProfileSettings?.hideUnconfiguredModels === true
|
|
|
823
862
|
if (process.stdin.isTTY && resetRawMode) process.stdin.setRawMode(false)
|
|
824
863
|
process.stdin.pause()
|
|
825
864
|
process.stdout.write(ALT_LEAVE)
|
|
865
|
+
if (process.stdout.isTTY) {
|
|
866
|
+
process.stdout.flush && process.stdout.flush()
|
|
867
|
+
}
|
|
826
868
|
}
|
|
827
869
|
|
|
828
870
|
const overlays = createOverlayRenderers(state, {
|
|
@@ -835,7 +877,6 @@ hideUnconfiguredModels: startupProfileSettings?.hideUnconfiguredModels === true
|
|
|
835
877
|
getProxySettings,
|
|
836
878
|
resolveApiKeys,
|
|
837
879
|
isProviderEnabled,
|
|
838
|
-
listProfiles,
|
|
839
880
|
TIER_CYCLE,
|
|
840
881
|
SETTINGS_OVERLAY_BG,
|
|
841
882
|
HELP_OVERLAY_BG,
|
|
@@ -877,11 +918,6 @@ hideUnconfiguredModels: startupProfileSettings?.hideUnconfiguredModels === true
|
|
|
877
918
|
removeApiKey,
|
|
878
919
|
persistApiKeysForProvider,
|
|
879
920
|
isProviderEnabled,
|
|
880
|
-
listProfiles,
|
|
881
|
-
loadProfile,
|
|
882
|
-
deleteProfile,
|
|
883
|
-
saveAsProfile,
|
|
884
|
-
setActiveProfile,
|
|
885
921
|
saveConfig,
|
|
886
922
|
getConfiguredInstallableProviders,
|
|
887
923
|
getInstallTargetModes,
|
|
@@ -980,15 +1016,21 @@ hideUnconfiguredModels: startupProfileSettings?.hideUnconfiguredModels === true
|
|
|
980
1016
|
? overlays.renderLog()
|
|
981
1017
|
: state.changelogOpen
|
|
982
1018
|
? overlays.renderChangelog()
|
|
983
|
-
|
|
1019
|
+
: renderTable(state.results, state.pendingPings, state.frame, state.cursor, state.sortColumn, state.sortDirection, state.pingInterval, state.lastPingTime, state.mode, state.tierFilterMode, state.scrollOffset, state.terminalRows, state.terminalCols, state.originFilterMode, state.proxyStartupStatus, state.pingMode, state.pingModeSource, state.hideUnconfiguredModels, state.widthWarningStartedAt, state.widthWarningDismissed, state.widthWarningShowCount, state.settingsUpdateState, state.settingsUpdateLatestVersion, getProxySettings(state.config).enabled === true, state.startupLatestVersion, state.versionAlertsEnabled)
|
|
984
1020
|
process.stdout.write(ALT_HOME + content)
|
|
1021
|
+
if (process.stdout.isTTY) {
|
|
1022
|
+
process.stdout.flush && process.stdout.flush()
|
|
1023
|
+
}
|
|
985
1024
|
}, Math.round(1000 / FPS))
|
|
986
1025
|
|
|
987
1026
|
// 📖 Populate visibleSorted before the first frame so Enter works immediately
|
|
988
1027
|
const initialVisible = state.results.filter(r => !r.hidden)
|
|
989
1028
|
state.visibleSorted = sortResultsWithPinnedFavorites(initialVisible, state.sortColumn, state.sortDirection)
|
|
990
1029
|
|
|
991
|
-
process.stdout.write(ALT_HOME + renderTable(state.results, state.pendingPings, state.frame, state.cursor, state.sortColumn, state.sortDirection, state.pingInterval, state.lastPingTime, state.mode, state.tierFilterMode, state.scrollOffset, state.terminalRows, state.terminalCols, state.originFilterMode, state.
|
|
1030
|
+
process.stdout.write(ALT_HOME + renderTable(state.results, state.pendingPings, state.frame, state.cursor, state.sortColumn, state.sortDirection, state.pingInterval, state.lastPingTime, state.mode, state.tierFilterMode, state.scrollOffset, state.terminalRows, state.terminalCols, state.originFilterMode, state.proxyStartupStatus, state.pingMode, state.pingModeSource, state.hideUnconfiguredModels, state.widthWarningStartedAt, state.widthWarningDismissed, state.widthWarningShowCount, state.settingsUpdateState, state.settingsUpdateLatestVersion, getProxySettings(state.config).enabled === true, state.startupLatestVersion, state.versionAlertsEnabled))
|
|
1031
|
+
if (process.stdout.isTTY) {
|
|
1032
|
+
process.stdout.flush && process.stdout.flush()
|
|
1033
|
+
}
|
|
992
1034
|
|
|
993
1035
|
// 📖 If --recommend was passed, auto-open the Smart Recommend overlay on start
|
|
994
1036
|
if (cliArgs.recommendMode) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "free-coding-models",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.11",
|
|
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/src/cli-help.js
CHANGED
|
@@ -26,10 +26,18 @@ const ANALYSIS_FLAGS = [
|
|
|
26
26
|
{ flag: '--json', description: 'Output results as JSON for scripts/automation' },
|
|
27
27
|
{ flag: '--tier <S|A|B|C>', description: 'Filter models by tier family' },
|
|
28
28
|
{ flag: '--recommend', description: 'Open Smart Recommend immediately on startup' },
|
|
29
|
+
{ flag: '--premium', description: 'Show only S/S+ models with perfect health and good verdict' },
|
|
30
|
+
{ flag: '--sort <column>', description: 'Sort by column (rank, tier, origin, model, ping, avg, swe, ctx, condition, verdict, uptime, stability, usage)' },
|
|
31
|
+
{ flag: '--desc | --asc', description: 'Set sort direction (descending or ascending)' },
|
|
32
|
+
{ flag: '--origin <provider>', description: 'Filter models by provider origin' },
|
|
33
|
+
{ flag: '--ping-interval <ms>', description: 'Override ping interval in milliseconds' },
|
|
34
|
+
{ flag: '--hide-unconfigured', description: 'Hide models without configured API keys' },
|
|
35
|
+
{ flag: '--show-unconfigured', description: 'Show all models regardless of API key config' },
|
|
36
|
+
{ flag: '--disable-widths-warning', description: 'Disable terminal width warning' },
|
|
29
37
|
]
|
|
30
38
|
|
|
31
39
|
const CONFIG_FLAGS = [
|
|
32
|
-
{ flag: '--
|
|
40
|
+
{ flag: '--proxy', description: 'Start FCM Proxy V2 in foreground with live dashboard (no daemon)' },
|
|
33
41
|
{ flag: '--no-telemetry', description: 'Disable anonymous telemetry for this run' },
|
|
34
42
|
{ flag: '--clean-proxy, --proxy-clean', description: 'Remove persisted fcm-proxy config from OpenCode' },
|
|
35
43
|
{ flag: '--help, -h', description: 'Print this help and exit' },
|