free-coding-models 0.1.51 → 0.1.52
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/bin/free-coding-models.js +129 -85
- package/package.json +1 -1
|
@@ -710,6 +710,7 @@ function renderTable(results, pendingPings, frame, cursor = null, sortColumn = '
|
|
|
710
710
|
lines.push(chalk.dim(` ↑↓ Navigate • `) + actionHint + chalk.dim(` • R/Y/O/M/L/A/S/C/H/V/U Sort • W↓/X↑ Interval (${intervalSec}s) • T Filter tier • Z Mode • `) + chalk.yellow('P') + chalk.dim(` Settings • Ctrl+C Exit`))
|
|
711
711
|
lines.push('')
|
|
712
712
|
lines.push(chalk.dim(' Made with ') + '💖 & ☕' + chalk.dim(' by ') + '\x1b]8;;https://github.com/vava-nessa\x1b\\vava-nessa\x1b]8;;\x1b\\' + chalk.dim(' • ') + '🫂 ' + chalk.cyanBright('\x1b]8;;https://discord.gg/5MbTnDC3Md\x1b\\Join our Discord!\x1b]8;;\x1b\\') + chalk.dim(' • ') + '⭐ ' + '\x1b]8;;https://github.com/vava-nessa/free-coding-models\x1b\\Read the docs on GitHub\x1b]8;;\x1b\\')
|
|
713
|
+
lines.push(chalk.dim(' 💬 Discord: ') + chalk.cyanBright('https://discord.gg/5MbTnDC3Md'))
|
|
713
714
|
lines.push('')
|
|
714
715
|
// 📖 Append \x1b[K (erase to EOL) to each line so leftover chars from previous
|
|
715
716
|
// 📖 frames are cleared. Then pad with blank cleared lines to fill the terminal,
|
|
@@ -758,6 +759,29 @@ const isWindows = process.platform === 'win32'
|
|
|
758
759
|
const isMac = process.platform === 'darwin'
|
|
759
760
|
const isLinux = process.platform === 'linux'
|
|
760
761
|
|
|
762
|
+
// ─── OpenCode model ID mapping ─────────────────────────────────────────────────
|
|
763
|
+
// 📖 Source model IDs -> OpenCode built-in model IDs (only where they differ)
|
|
764
|
+
// 📖 Groq's API aliases short names to full names, but OpenCode does exact ID matching
|
|
765
|
+
// 📖 against its built-in model list. Unmapped models pass through as-is.
|
|
766
|
+
const OPENCODE_MODEL_MAP = {
|
|
767
|
+
groq: {
|
|
768
|
+
'moonshotai/kimi-k2-instruct': 'moonshotai/kimi-k2-instruct-0905',
|
|
769
|
+
'meta-llama/llama-4-scout-17b-16e-preview': 'meta-llama/llama-4-scout-17b-16e-instruct',
|
|
770
|
+
'meta-llama/llama-4-maverick-17b-128e-preview': 'meta-llama/llama-4-maverick-17b-128e-instruct',
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
function getOpenCodeModelId(providerKey, modelId) {
|
|
775
|
+
return OPENCODE_MODEL_MAP[providerKey]?.[modelId] || modelId
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
// 📖 Env var names per provider -- used for passing resolved keys to child processes
|
|
779
|
+
const ENV_VAR_NAMES = {
|
|
780
|
+
nvidia: 'NVIDIA_API_KEY',
|
|
781
|
+
groq: 'GROQ_API_KEY',
|
|
782
|
+
cerebras: 'CEREBRAS_API_KEY',
|
|
783
|
+
}
|
|
784
|
+
|
|
761
785
|
// 📖 OpenCode config location varies by platform
|
|
762
786
|
// 📖 Windows: %APPDATA%\opencode\opencode.json (or sometimes ~/.config/opencode)
|
|
763
787
|
// 📖 macOS/Linux: ~/.config/opencode/opencode.json
|
|
@@ -809,16 +833,50 @@ function checkNvidiaNimConfig() {
|
|
|
809
833
|
)
|
|
810
834
|
}
|
|
811
835
|
|
|
836
|
+
// ─── Shared OpenCode spawn helper ──────────────────────────────────────────────
|
|
837
|
+
// 📖 Resolves the actual API key from config/env and passes it as an env var
|
|
838
|
+
// 📖 to the child process so OpenCode's {env:GROQ_API_KEY} references work
|
|
839
|
+
// 📖 even when the key is only in ~/.free-coding-models.json (not in shell env).
|
|
840
|
+
async function spawnOpenCode(args, providerKey, fcmConfig) {
|
|
841
|
+
const envVarName = ENV_VAR_NAMES[providerKey]
|
|
842
|
+
const resolvedKey = getApiKey(fcmConfig, providerKey)
|
|
843
|
+
const childEnv = { ...process.env }
|
|
844
|
+
if (envVarName && resolvedKey) childEnv[envVarName] = resolvedKey
|
|
845
|
+
|
|
846
|
+
const { spawn } = await import('child_process')
|
|
847
|
+
const child = spawn('opencode', args, {
|
|
848
|
+
stdio: 'inherit',
|
|
849
|
+
shell: true,
|
|
850
|
+
detached: false,
|
|
851
|
+
env: childEnv
|
|
852
|
+
})
|
|
853
|
+
|
|
854
|
+
return new Promise((resolve, reject) => {
|
|
855
|
+
child.on('exit', resolve)
|
|
856
|
+
child.on('error', (err) => {
|
|
857
|
+
if (err.code === 'ENOENT') {
|
|
858
|
+
console.error(chalk.red('\n X Could not find "opencode" -- is it installed and in your PATH?'))
|
|
859
|
+
console.error(chalk.dim(' Install: npm i -g opencode or see https://opencode.ai'))
|
|
860
|
+
resolve(1)
|
|
861
|
+
} else {
|
|
862
|
+
reject(err)
|
|
863
|
+
}
|
|
864
|
+
})
|
|
865
|
+
})
|
|
866
|
+
}
|
|
867
|
+
|
|
812
868
|
// ─── Start OpenCode ────────────────────────────────────────────────────────────
|
|
813
869
|
// 📖 Launches OpenCode with the selected model.
|
|
814
870
|
// 📖 Handles all 3 providers: nvidia (needs custom provider config), groq & cerebras (built-in in OpenCode).
|
|
815
871
|
// 📖 For nvidia: checks if NIM is configured, sets provider.models entry, spawns with nvidia/model-id.
|
|
816
|
-
// 📖 For groq/cerebras: OpenCode has built-in support
|
|
872
|
+
// 📖 For groq/cerebras: OpenCode has built-in support -- just sets model in config and spawns.
|
|
817
873
|
// 📖 Model format: { modelId, label, tier, providerKey }
|
|
818
|
-
|
|
874
|
+
// 📖 fcmConfig: the free-coding-models config (for resolving API keys)
|
|
875
|
+
async function startOpenCode(model, fcmConfig) {
|
|
819
876
|
const providerKey = model.providerKey ?? 'nvidia'
|
|
820
|
-
// 📖
|
|
821
|
-
const
|
|
877
|
+
// 📖 Map model ID to OpenCode's built-in ID if it differs from our source ID
|
|
878
|
+
const ocModelId = getOpenCodeModelId(providerKey, model.modelId)
|
|
879
|
+
const modelRef = `${providerKey}/${ocModelId}`
|
|
822
880
|
|
|
823
881
|
if (providerKey === 'nvidia') {
|
|
824
882
|
// 📖 NVIDIA NIM needs a custom provider block in OpenCode config (not built-in)
|
|
@@ -840,11 +898,9 @@ async function startOpenCode(model) {
|
|
|
840
898
|
config.model = modelRef
|
|
841
899
|
|
|
842
900
|
// 📖 Register the model in the nvidia provider's models section
|
|
843
|
-
// 📖 OpenCode requires models to be explicitly listed in provider.models
|
|
844
|
-
// 📖 to recognize them — without this, it falls back to the previous default
|
|
845
901
|
if (config.provider?.nvidia) {
|
|
846
902
|
if (!config.provider.nvidia.models) config.provider.nvidia.models = {}
|
|
847
|
-
config.provider.nvidia.models[
|
|
903
|
+
config.provider.nvidia.models[ocModelId] = { name: model.label }
|
|
848
904
|
}
|
|
849
905
|
|
|
850
906
|
saveOpenCodeConfig(config)
|
|
@@ -863,27 +919,9 @@ async function startOpenCode(model) {
|
|
|
863
919
|
console.log(chalk.dim(' Starting OpenCode…'))
|
|
864
920
|
console.log()
|
|
865
921
|
|
|
866
|
-
|
|
867
|
-
const child = spawn('opencode', ['--model', modelRef], {
|
|
868
|
-
stdio: 'inherit',
|
|
869
|
-
shell: true,
|
|
870
|
-
detached: false
|
|
871
|
-
})
|
|
872
|
-
|
|
873
|
-
await new Promise((resolve, reject) => {
|
|
874
|
-
child.on('exit', resolve)
|
|
875
|
-
child.on('error', (err) => {
|
|
876
|
-
if (err.code === 'ENOENT') {
|
|
877
|
-
console.error(chalk.red('\n ✗ Could not find "opencode" — is it installed and in your PATH?'))
|
|
878
|
-
console.error(chalk.dim(' Install: npm i -g opencode or see https://opencode.ai'))
|
|
879
|
-
resolve(1)
|
|
880
|
-
} else {
|
|
881
|
-
reject(err)
|
|
882
|
-
}
|
|
883
|
-
})
|
|
884
|
-
})
|
|
922
|
+
await spawnOpenCode(['--model', modelRef], providerKey, fcmConfig)
|
|
885
923
|
} else {
|
|
886
|
-
// 📖 NVIDIA NIM not configured
|
|
924
|
+
// 📖 NVIDIA NIM not configured -- show install prompt
|
|
887
925
|
console.log(chalk.yellow(' ⚠ NVIDIA NIM not configured in OpenCode'))
|
|
888
926
|
console.log()
|
|
889
927
|
console.log(chalk.dim(' Starting OpenCode with installation prompt…'))
|
|
@@ -914,29 +952,11 @@ After installation, you can use: opencode --model ${modelRef}`
|
|
|
914
952
|
console.log(chalk.dim(' Starting OpenCode…'))
|
|
915
953
|
console.log()
|
|
916
954
|
|
|
917
|
-
|
|
918
|
-
const child = spawn('opencode', [], {
|
|
919
|
-
stdio: 'inherit',
|
|
920
|
-
shell: true,
|
|
921
|
-
detached: false
|
|
922
|
-
})
|
|
923
|
-
|
|
924
|
-
await new Promise((resolve, reject) => {
|
|
925
|
-
child.on('exit', resolve)
|
|
926
|
-
child.on('error', (err) => {
|
|
927
|
-
if (err.code === 'ENOENT') {
|
|
928
|
-
console.error(chalk.red('\n ✗ Could not find "opencode" — is it installed and in your PATH?'))
|
|
929
|
-
console.error(chalk.dim(' Install: npm i -g opencode or see https://opencode.ai'))
|
|
930
|
-
resolve(1)
|
|
931
|
-
} else {
|
|
932
|
-
reject(err)
|
|
933
|
-
}
|
|
934
|
-
})
|
|
935
|
-
})
|
|
955
|
+
await spawnOpenCode([], providerKey, fcmConfig)
|
|
936
956
|
}
|
|
937
957
|
} else {
|
|
938
|
-
// 📖 Groq: built-in OpenCode provider
|
|
939
|
-
// 📖 Cerebras: NOT built-in
|
|
958
|
+
// 📖 Groq: built-in OpenCode provider -- needs provider block with apiKey in opencode.json.
|
|
959
|
+
// 📖 Cerebras: NOT built-in -- needs @ai-sdk/openai-compatible + baseURL, like NVIDIA.
|
|
940
960
|
// 📖 Both need the model registered in provider.<key>.models so OpenCode can find it.
|
|
941
961
|
console.log(chalk.green(` 🚀 Setting ${chalk.bold(model.label)} as default…`))
|
|
942
962
|
console.log(chalk.dim(` Model: ${modelRef}`))
|
|
@@ -974,9 +994,12 @@ After installation, you can use: opencode --model ${modelRef}`
|
|
|
974
994
|
}
|
|
975
995
|
|
|
976
996
|
// 📖 Register the model in the provider's models section
|
|
977
|
-
// 📖
|
|
978
|
-
|
|
979
|
-
|
|
997
|
+
// 📖 Only register custom models -- skip if the model maps to a built-in OpenCode ID
|
|
998
|
+
const isBuiltinMapped = OPENCODE_MODEL_MAP[providerKey]?.[model.modelId]
|
|
999
|
+
if (!isBuiltinMapped) {
|
|
1000
|
+
if (!config.provider[providerKey].models) config.provider[providerKey].models = {}
|
|
1001
|
+
config.provider[providerKey].models[ocModelId] = { name: model.label }
|
|
1002
|
+
}
|
|
980
1003
|
|
|
981
1004
|
config.model = modelRef
|
|
982
1005
|
saveOpenCodeConfig(config)
|
|
@@ -995,25 +1018,7 @@ After installation, you can use: opencode --model ${modelRef}`
|
|
|
995
1018
|
console.log(chalk.dim(' Starting OpenCode…'))
|
|
996
1019
|
console.log()
|
|
997
1020
|
|
|
998
|
-
|
|
999
|
-
const child = spawn('opencode', ['--model', modelRef], {
|
|
1000
|
-
stdio: 'inherit',
|
|
1001
|
-
shell: true,
|
|
1002
|
-
detached: false
|
|
1003
|
-
})
|
|
1004
|
-
|
|
1005
|
-
await new Promise((resolve, reject) => {
|
|
1006
|
-
child.on('exit', resolve)
|
|
1007
|
-
child.on('error', (err) => {
|
|
1008
|
-
if (err.code === 'ENOENT') {
|
|
1009
|
-
console.error(chalk.red('\n ✗ Could not find "opencode" — is it installed and in your PATH?'))
|
|
1010
|
-
console.error(chalk.dim(' Install: npm i -g opencode or see https://opencode.ai'))
|
|
1011
|
-
resolve(1)
|
|
1012
|
-
} else {
|
|
1013
|
-
reject(err)
|
|
1014
|
-
}
|
|
1015
|
-
})
|
|
1016
|
-
})
|
|
1021
|
+
await spawnOpenCode(['--model', modelRef], providerKey, fcmConfig)
|
|
1017
1022
|
}
|
|
1018
1023
|
}
|
|
1019
1024
|
|
|
@@ -1022,10 +1027,11 @@ After installation, you can use: opencode --model ${modelRef}`
|
|
|
1022
1027
|
// 📖 OpenCode Desktop shares config at the same location as CLI.
|
|
1023
1028
|
// 📖 Handles all 3 providers: nvidia (needs custom provider config), groq & cerebras (built-in).
|
|
1024
1029
|
// 📖 No need to wait for exit — Desktop app stays open independently.
|
|
1025
|
-
async function startOpenCodeDesktop(model) {
|
|
1030
|
+
async function startOpenCodeDesktop(model, fcmConfig) {
|
|
1026
1031
|
const providerKey = model.providerKey ?? 'nvidia'
|
|
1027
|
-
// 📖
|
|
1028
|
-
const
|
|
1032
|
+
// 📖 Map model ID to OpenCode's built-in ID if it differs from our source ID
|
|
1033
|
+
const ocModelId = getOpenCodeModelId(providerKey, model.modelId)
|
|
1034
|
+
const modelRef = `${providerKey}/${ocModelId}`
|
|
1029
1035
|
|
|
1030
1036
|
// 📖 Helper to open the Desktop app based on platform
|
|
1031
1037
|
const launchDesktop = async () => {
|
|
@@ -1074,7 +1080,7 @@ async function startOpenCodeDesktop(model) {
|
|
|
1074
1080
|
|
|
1075
1081
|
if (config.provider?.nvidia) {
|
|
1076
1082
|
if (!config.provider.nvidia.models) config.provider.nvidia.models = {}
|
|
1077
|
-
config.provider.nvidia.models[
|
|
1083
|
+
config.provider.nvidia.models[ocModelId] = { name: model.label }
|
|
1078
1084
|
}
|
|
1079
1085
|
|
|
1080
1086
|
saveOpenCodeConfig(config)
|
|
@@ -1157,8 +1163,12 @@ ${isWindows ? 'set NVIDIA_API_KEY=your_key_here' : 'export NVIDIA_API_KEY=your_k
|
|
|
1157
1163
|
}
|
|
1158
1164
|
|
|
1159
1165
|
// 📖 Register the model in the provider's models section
|
|
1160
|
-
|
|
1161
|
-
|
|
1166
|
+
// 📖 Only register custom models -- skip if the model maps to a built-in OpenCode ID
|
|
1167
|
+
const isBuiltinMapped = OPENCODE_MODEL_MAP[providerKey]?.[model.modelId]
|
|
1168
|
+
if (!isBuiltinMapped) {
|
|
1169
|
+
if (!config.provider[providerKey].models) config.provider[providerKey].models = {}
|
|
1170
|
+
config.provider[providerKey].models[ocModelId] = { name: model.label }
|
|
1171
|
+
}
|
|
1162
1172
|
|
|
1163
1173
|
config.model = modelRef
|
|
1164
1174
|
saveOpenCodeConfig(config)
|
|
@@ -1243,9 +1253,14 @@ async function startOpenClaw(model, apiKey) {
|
|
|
1243
1253
|
config.models.providers.nvidia = {
|
|
1244
1254
|
baseUrl: 'https://integrate.api.nvidia.com/v1',
|
|
1245
1255
|
api: 'openai-completions',
|
|
1256
|
+
models: [],
|
|
1246
1257
|
}
|
|
1247
1258
|
console.log(chalk.dim(' ➕ Added nvidia provider block to OpenClaw config (models.providers.nvidia)'))
|
|
1248
1259
|
}
|
|
1260
|
+
// 📖 Ensure models array exists even if the provider block was created by an older version
|
|
1261
|
+
if (!Array.isArray(config.models.providers.nvidia.models)) {
|
|
1262
|
+
config.models.providers.nvidia.models = []
|
|
1263
|
+
}
|
|
1249
1264
|
|
|
1250
1265
|
// 📖 Store API key in the root "env" section so OpenClaw can read it as NVIDIA_API_KEY env var.
|
|
1251
1266
|
// 📖 Only writes if not already set to avoid overwriting an existing key.
|
|
@@ -1450,7 +1465,7 @@ async function main() {
|
|
|
1450
1465
|
// 📖 Clamp scrollOffset so cursor is always within the visible viewport window.
|
|
1451
1466
|
// 📖 Called after every cursor move, sort change, and terminal resize.
|
|
1452
1467
|
const adjustScrollOffset = (st) => {
|
|
1453
|
-
const total = st.results.length
|
|
1468
|
+
const total = st.visibleSorted ? st.visibleSorted.length : st.results.filter(r => !r.hidden).length
|
|
1454
1469
|
let maxSlots = st.terminalRows - 10 // 5 header + 5 footer
|
|
1455
1470
|
if (maxSlots < 1) maxSlots = 1
|
|
1456
1471
|
if (total <= maxSlots) { st.scrollOffset = 0; return }
|
|
@@ -1497,6 +1512,7 @@ async function main() {
|
|
|
1497
1512
|
settingsEditBuffer: '', // 📖 Typed characters for the API key being edited
|
|
1498
1513
|
settingsTestResults: {}, // 📖 { providerKey: 'pending'|'ok'|'fail'|null }
|
|
1499
1514
|
config, // 📖 Live reference to the config object (updated on save)
|
|
1515
|
+
visibleSorted: [], // 📖 Cached visible+sorted models — shared between render loop and key handlers
|
|
1500
1516
|
}
|
|
1501
1517
|
|
|
1502
1518
|
// 📖 Re-clamp viewport on terminal resize
|
|
@@ -1753,7 +1769,11 @@ async function main() {
|
|
|
1753
1769
|
state.sortColumn = col
|
|
1754
1770
|
state.sortDirection = 'asc'
|
|
1755
1771
|
}
|
|
1756
|
-
|
|
1772
|
+
// 📖 Recompute visible sorted list and reset cursor to top to avoid stale index
|
|
1773
|
+
const visible = state.results.filter(r => !r.hidden)
|
|
1774
|
+
state.visibleSorted = sortResults(visible, state.sortColumn, state.sortDirection)
|
|
1775
|
+
state.cursor = 0
|
|
1776
|
+
state.scrollOffset = 0
|
|
1757
1777
|
return
|
|
1758
1778
|
}
|
|
1759
1779
|
|
|
@@ -1769,7 +1789,11 @@ async function main() {
|
|
|
1769
1789
|
if (key.name === 't') {
|
|
1770
1790
|
tierFilterMode = (tierFilterMode + 1) % TIER_CYCLE.length
|
|
1771
1791
|
applyTierFilter()
|
|
1772
|
-
|
|
1792
|
+
// 📖 Recompute visible sorted list and reset cursor to avoid stale index into new filtered set
|
|
1793
|
+
const visible = state.results.filter(r => !r.hidden)
|
|
1794
|
+
state.visibleSorted = sortResults(visible, state.sortColumn, state.sortDirection)
|
|
1795
|
+
state.cursor = 0
|
|
1796
|
+
state.scrollOffset = 0
|
|
1773
1797
|
return
|
|
1774
1798
|
}
|
|
1775
1799
|
|
|
@@ -1796,7 +1820,7 @@ async function main() {
|
|
|
1796
1820
|
}
|
|
1797
1821
|
|
|
1798
1822
|
if (key.name === 'down') {
|
|
1799
|
-
if (state.cursor <
|
|
1823
|
+
if (state.cursor < state.visibleSorted.length - 1) {
|
|
1800
1824
|
state.cursor++
|
|
1801
1825
|
adjustScrollOffset(state)
|
|
1802
1826
|
}
|
|
@@ -1809,9 +1833,9 @@ async function main() {
|
|
|
1809
1833
|
}
|
|
1810
1834
|
|
|
1811
1835
|
if (key.name === 'return') { // Enter
|
|
1812
|
-
// 📖 Use the
|
|
1813
|
-
const
|
|
1814
|
-
|
|
1836
|
+
// 📖 Use the cached visible+sorted array — guaranteed to match what's on screen
|
|
1837
|
+
const selected = state.visibleSorted[state.cursor]
|
|
1838
|
+
if (!selected) return // 📖 Guard: empty visible list (all filtered out)
|
|
1815
1839
|
// 📖 Allow selecting ANY model (even timeout/down) - user knows what they're doing
|
|
1816
1840
|
userSelected = { modelId: selected.modelId, label: selected.label, tier: selected.tier, providerKey: selected.providerKey }
|
|
1817
1841
|
|
|
@@ -1834,13 +1858,24 @@ async function main() {
|
|
|
1834
1858
|
}
|
|
1835
1859
|
console.log()
|
|
1836
1860
|
|
|
1861
|
+
// 📖 Warn if no API key is configured for the selected model's provider
|
|
1862
|
+
if (state.mode !== 'openclaw') {
|
|
1863
|
+
const selectedApiKey = getApiKey(state.config, selected.providerKey)
|
|
1864
|
+
if (!selectedApiKey) {
|
|
1865
|
+
console.log(chalk.yellow(` Warning: No API key configured for ${selected.providerKey}.`))
|
|
1866
|
+
console.log(chalk.yellow(` OpenCode may not be able to use ${selected.label}.`))
|
|
1867
|
+
console.log(chalk.dim(` Set ${ENV_VAR_NAMES[selected.providerKey] || selected.providerKey.toUpperCase() + '_API_KEY'} or configure via settings (P key).`))
|
|
1868
|
+
console.log()
|
|
1869
|
+
}
|
|
1870
|
+
}
|
|
1871
|
+
|
|
1837
1872
|
// 📖 Dispatch to the correct integration based on active mode
|
|
1838
1873
|
if (state.mode === 'openclaw') {
|
|
1839
1874
|
await startOpenClaw(userSelected, apiKey)
|
|
1840
1875
|
} else if (state.mode === 'opencode-desktop') {
|
|
1841
|
-
await startOpenCodeDesktop(userSelected)
|
|
1876
|
+
await startOpenCodeDesktop(userSelected, state.config)
|
|
1842
1877
|
} else {
|
|
1843
|
-
await startOpenCode(userSelected)
|
|
1878
|
+
await startOpenCode(userSelected, state.config)
|
|
1844
1879
|
}
|
|
1845
1880
|
process.exit(0)
|
|
1846
1881
|
}
|
|
@@ -1857,12 +1892,21 @@ async function main() {
|
|
|
1857
1892
|
// 📖 Animation loop: render settings overlay OR main table based on state
|
|
1858
1893
|
const ticker = setInterval(() => {
|
|
1859
1894
|
state.frame++
|
|
1895
|
+
// 📖 Cache visible+sorted models each frame so Enter handler always matches the display
|
|
1896
|
+
if (!state.settingsOpen) {
|
|
1897
|
+
const visible = state.results.filter(r => !r.hidden)
|
|
1898
|
+
state.visibleSorted = sortResults(visible, state.sortColumn, state.sortDirection)
|
|
1899
|
+
}
|
|
1860
1900
|
const content = state.settingsOpen
|
|
1861
1901
|
? renderSettings()
|
|
1862
1902
|
: renderTable(state.results, state.pendingPings, state.frame, state.cursor, state.sortColumn, state.sortDirection, state.pingInterval, state.lastPingTime, state.mode, tierFilterMode, state.scrollOffset, state.terminalRows)
|
|
1863
1903
|
process.stdout.write(ALT_HOME + content)
|
|
1864
1904
|
}, Math.round(1000 / FPS))
|
|
1865
1905
|
|
|
1906
|
+
// 📖 Populate visibleSorted before the first frame so Enter works immediately
|
|
1907
|
+
const initialVisible = state.results.filter(r => !r.hidden)
|
|
1908
|
+
state.visibleSorted = sortResults(initialVisible, state.sortColumn, state.sortDirection)
|
|
1909
|
+
|
|
1866
1910
|
process.stdout.write(ALT_HOME + renderTable(state.results, state.pendingPings, state.frame, state.cursor, state.sortColumn, state.sortDirection, state.pingInterval, state.lastPingTime, state.mode, tierFilterMode, state.scrollOffset, state.terminalRows))
|
|
1867
1911
|
|
|
1868
1912
|
// ── Continuous ping loop — ping all models every N seconds forever ──────────
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "free-coding-models",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.52",
|
|
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",
|