free-coding-models 0.1.19 β 0.1.20
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/README.md +12 -1
- package/bin/free-coding-models.js +67 -6
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
- **π¨ Clean output** β Zero scrollback pollution, interface stays open until Ctrl+C
|
|
57
57
|
- **πΆ Status indicators** β UP β
Β· Timeout β³ Β· Overloaded π₯ Β· Not Found π«
|
|
58
58
|
- **π§ Multi-source support** β Extensible architecture via `sources.js` (add new providers easily)
|
|
59
|
-
- **π· Tier filtering** β Filter models by tier letter (S, A, B, C) with `--tier`
|
|
59
|
+
- **π· Tier filtering** β Filter models by tier letter (S, A, B, C) with `--tier` flag or dynamically with E/D keys
|
|
60
60
|
|
|
61
61
|
---
|
|
62
62
|
|
|
@@ -226,6 +226,15 @@ free-coding-models --tier B # Only B+, B (lightweight options)
|
|
|
226
226
|
free-coding-models --tier C # Only C (edge/minimal models)
|
|
227
227
|
```
|
|
228
228
|
|
|
229
|
+
#### Dynamic tier filtering with E/D keys
|
|
230
|
+
|
|
231
|
+
During runtime, use **E** and **D** keys to dynamically adjust the tier filter:
|
|
232
|
+
|
|
233
|
+
- **E** (Elevate) β Show fewer, higher-tier models (cycle: All β S β A β B β C β All)
|
|
234
|
+
- **D** (Descend) β Show more, lower-tier models (cycle: All β C β B β A β S β All)
|
|
235
|
+
|
|
236
|
+
Current tier filter is shown in the header badge (e.g., `[Tier S]`)
|
|
237
|
+
|
|
229
238
|
---
|
|
230
239
|
|
|
231
240
|
## π OpenCode Integration
|
|
@@ -439,6 +448,8 @@ This script:
|
|
|
439
448
|
- **R/T/O/M/P/A/S/V/U** β Sort by Rank/Tier/Origin/Model/Ping/Avg/Status/Verdict/Uptime
|
|
440
449
|
- **W** β Decrease ping interval (faster pings)
|
|
441
450
|
- **X** β Increase ping interval (slower pings)
|
|
451
|
+
- **E** β Elevate tier filter (show fewer, higher-tier models)
|
|
452
|
+
- **D** β Descend tier filter (show more, lower-tier models)
|
|
442
453
|
- **Ctrl+C** β Exit
|
|
443
454
|
|
|
444
455
|
---
|
|
@@ -327,7 +327,7 @@ const spinCell = (f, o = 0) => chalk.dim.yellow(FRAMES[(f + o) % FRAMES.length].
|
|
|
327
327
|
// π are imported from lib/utils.js for testability
|
|
328
328
|
|
|
329
329
|
// π renderTable: mode param controls footer hint text (opencode vs openclaw)
|
|
330
|
-
function renderTable(results, pendingPings, frame, cursor = null, sortColumn = 'avg', sortDirection = 'asc', pingInterval = PING_INTERVAL, lastPingTime = Date.now(), mode = 'opencode') {
|
|
330
|
+
function renderTable(results, pendingPings, frame, cursor = null, sortColumn = 'avg', sortDirection = 'asc', pingInterval = PING_INTERVAL, lastPingTime = Date.now(), mode = 'opencode', tierFilter = null) {
|
|
331
331
|
const up = results.filter(r => r.status === 'up').length
|
|
332
332
|
const down = results.filter(r => r.status === 'down').length
|
|
333
333
|
const timeout = results.filter(r => r.status === 'timeout').length
|
|
@@ -354,6 +354,12 @@ function renderTable(results, pendingPings, frame, cursor = null, sortColumn = '
|
|
|
354
354
|
modeBadge = chalk.bold.rgb(0, 200, 255)(' [π» CLI]')
|
|
355
355
|
}
|
|
356
356
|
|
|
357
|
+
// π Tier filter badge shown when filtering is active
|
|
358
|
+
let tierBadge = ''
|
|
359
|
+
if (tierFilter) {
|
|
360
|
+
tierBadge = chalk.bold.rgb(255, 200, 0)(` [Tier ${tierFilter}]`)
|
|
361
|
+
}
|
|
362
|
+
|
|
357
363
|
// π Column widths (generous spacing with margins)
|
|
358
364
|
const W_RANK = 6
|
|
359
365
|
const W_TIER = 6
|
|
@@ -370,7 +376,7 @@ function renderTable(results, pendingPings, frame, cursor = null, sortColumn = '
|
|
|
370
376
|
|
|
371
377
|
const lines = [
|
|
372
378
|
'',
|
|
373
|
-
` ${chalk.bold('β‘ Free Coding Models')} ${chalk.dim('v' + LOCAL_VERSION)}${modeBadge} ` +
|
|
379
|
+
` ${chalk.bold('β‘ Free Coding Models')} ${chalk.dim('v' + LOCAL_VERSION)}${modeBadge}${tierBadge} ` +
|
|
374
380
|
chalk.greenBright(`β
${up}`) + chalk.dim(' up ') +
|
|
375
381
|
chalk.yellow(`β± ${timeout}`) + chalk.dim(' timeout ') +
|
|
376
382
|
chalk.red(`β ${down}`) + chalk.dim(' down ') +
|
|
@@ -556,7 +562,7 @@ function renderTable(results, pendingPings, frame, cursor = null, sortColumn = '
|
|
|
556
562
|
: mode === 'opencode-desktop'
|
|
557
563
|
? chalk.rgb(0, 200, 255)('EnterβOpenDesktop')
|
|
558
564
|
: chalk.rgb(0, 200, 255)('EnterβOpenCode')
|
|
559
|
-
lines.push(chalk.dim(` ββ Navigate β’ `) + actionHint + chalk.dim(` β’ R/T/O/M/P/A/S/V/U Sort β’ Wβ/Xβ Interval (${intervalSec}s) β’ Ctrl+C Exit`))
|
|
565
|
+
lines.push(chalk.dim(` ββ Navigate β’ `) + actionHint + chalk.dim(` β’ R/T/O/M/P/A/S/V/U Sort β’ Wβ/Xβ Interval (${intervalSec}s) β’ Eβ/Dβ Tier β’ Ctrl+C Exit`))
|
|
560
566
|
lines.push('')
|
|
561
567
|
lines.push(chalk.dim(' made with ') + 'π©·' + chalk.dim(' by vava-nessa β’ ') + chalk.dim.underline('https://github.com/vava-nessa/free-coding-models'))
|
|
562
568
|
lines.push('')
|
|
@@ -1070,6 +1076,7 @@ async function main() {
|
|
|
1070
1076
|
// π sortColumn: 'rank'|'tier'|'origin'|'model'|'ping'|'avg'|'status'|'verdict'|'uptime'
|
|
1071
1077
|
// π sortDirection: 'asc' (default) or 'desc'
|
|
1072
1078
|
// π pingInterval: current interval in ms (default 2000, adjustable with W/X keys)
|
|
1079
|
+
// π tierFilter: current tier filter letter (null = all, 'S' = S+/S, 'A' = A+/A/A-, etc.)
|
|
1073
1080
|
const state = {
|
|
1074
1081
|
results,
|
|
1075
1082
|
pendingPings: 0,
|
|
@@ -1082,6 +1089,7 @@ async function main() {
|
|
|
1082
1089
|
lastPingTime: Date.now(), // π Track when last ping cycle started
|
|
1083
1090
|
fiableMode, // π Pass fiable mode to state
|
|
1084
1091
|
mode, // π 'opencode' or 'openclaw' β controls Enter action
|
|
1092
|
+
tierFilter: tierFilter || null, // π Track current tier filter (null if no --tier flag)
|
|
1085
1093
|
}
|
|
1086
1094
|
|
|
1087
1095
|
// π Enter alternate screen β animation runs here, zero scrollback pollution
|
|
@@ -1097,6 +1105,40 @@ async function main() {
|
|
|
1097
1105
|
process.on('SIGINT', () => exit(0))
|
|
1098
1106
|
process.on('SIGTERM', () => exit(0))
|
|
1099
1107
|
|
|
1108
|
+
// π Apply tier filter to results based on current state.tierFilter
|
|
1109
|
+
// π Preserves existing ping history when filtering
|
|
1110
|
+
function applyTierFilter() {
|
|
1111
|
+
const allModels = MODELS.map(([modelId, label, tier], i) => ({
|
|
1112
|
+
idx: i + 1, modelId, label, tier,
|
|
1113
|
+
status: 'pending',
|
|
1114
|
+
pings: [],
|
|
1115
|
+
httpCode: null,
|
|
1116
|
+
}))
|
|
1117
|
+
|
|
1118
|
+
if (!state.tierFilter) {
|
|
1119
|
+
return allModels
|
|
1120
|
+
}
|
|
1121
|
+
|
|
1122
|
+
// π Filter models by tier and preserve existing ping history
|
|
1123
|
+
const filteredModels = allModels.filter(model =>
|
|
1124
|
+
TIER_LETTER_MAP[state.tierFilter].includes(model.tier)
|
|
1125
|
+
)
|
|
1126
|
+
|
|
1127
|
+
// π Try to preserve existing ping data from current results
|
|
1128
|
+
return filteredModels.map(model => {
|
|
1129
|
+
const existingResult = state.results.find(r => r.modelId === model.modelId)
|
|
1130
|
+
if (existingResult) {
|
|
1131
|
+
return {
|
|
1132
|
+
...model,
|
|
1133
|
+
status: existingResult.status,
|
|
1134
|
+
pings: [...existingResult.pings],
|
|
1135
|
+
httpCode: existingResult.httpCode
|
|
1136
|
+
}
|
|
1137
|
+
}
|
|
1138
|
+
return model
|
|
1139
|
+
})
|
|
1140
|
+
}
|
|
1141
|
+
|
|
1100
1142
|
// π Setup keyboard input for interactive selection during pings
|
|
1101
1143
|
// π Use readline with keypress event for arrow key handling
|
|
1102
1144
|
process.stdin.setEncoding('utf8')
|
|
@@ -1129,7 +1171,26 @@ async function main() {
|
|
|
1129
1171
|
// π Minimum 1s, maximum 60s
|
|
1130
1172
|
if (key.name === 'w') {
|
|
1131
1173
|
state.pingInterval = Math.max(1000, state.pingInterval - 1000)
|
|
1132
|
-
|
|
1174
|
+
} else if (key.name === 'x') {
|
|
1175
|
+
state.pingInterval = Math.min(60000, state.pingInterval + 1000)
|
|
1176
|
+
}
|
|
1177
|
+
|
|
1178
|
+
// π Tier filtering keys: E=elevate (more restrictive), D=descend (less restrictive)
|
|
1179
|
+
// π Cycle through: null β 'S' β 'A' β 'B' β 'C' β null (all tiers)
|
|
1180
|
+
if (key.name === 'e') {
|
|
1181
|
+
const tierOrder = [null, 'S', 'A', 'B', 'C']
|
|
1182
|
+
const currentIndex = tierOrder.indexOf(state.tierFilter)
|
|
1183
|
+
const nextIndex = (currentIndex + 1) % tierOrder.length
|
|
1184
|
+
state.tierFilter = tierOrder[nextIndex]
|
|
1185
|
+
state.results = applyTierFilter()
|
|
1186
|
+
state.cursor = Math.min(state.cursor, state.results.length - 1)
|
|
1187
|
+
} else if (key.name === 'd') {
|
|
1188
|
+
const tierOrder = [null, 'C', 'B', 'A', 'S']
|
|
1189
|
+
const currentIndex = tierOrder.indexOf(state.tierFilter)
|
|
1190
|
+
const nextIndex = (currentIndex - 1 + tierOrder.length) % tierOrder.length
|
|
1191
|
+
state.tierFilter = tierOrder[nextIndex]
|
|
1192
|
+
state.results = applyTierFilter()
|
|
1193
|
+
state.cursor = Math.min(state.cursor, state.results.length - 1)
|
|
1133
1194
|
}
|
|
1134
1195
|
|
|
1135
1196
|
if (key.name === 'x') {
|
|
@@ -1205,10 +1266,10 @@ async function main() {
|
|
|
1205
1266
|
// π Animation loop: clear alt screen + redraw table at FPS with cursor
|
|
1206
1267
|
const ticker = setInterval(() => {
|
|
1207
1268
|
state.frame++
|
|
1208
|
-
process.stdout.write(ALT_CLEAR + renderTable(state.results, state.pendingPings, state.frame, state.cursor, state.sortColumn, state.sortDirection, state.pingInterval, state.lastPingTime, state.mode))
|
|
1269
|
+
process.stdout.write(ALT_CLEAR + renderTable(state.results, state.pendingPings, state.frame, state.cursor, state.sortColumn, state.sortDirection, state.pingInterval, state.lastPingTime, state.mode, state.tierFilter))
|
|
1209
1270
|
}, Math.round(1000 / FPS))
|
|
1210
1271
|
|
|
1211
|
-
process.stdout.write(ALT_CLEAR + renderTable(state.results, state.pendingPings, state.frame, state.cursor, state.sortColumn, state.sortDirection, state.pingInterval, state.lastPingTime, state.mode))
|
|
1272
|
+
process.stdout.write(ALT_CLEAR + renderTable(state.results, state.pendingPings, state.frame, state.cursor, state.sortColumn, state.sortDirection, state.pingInterval, state.lastPingTime, state.mode, state.tierFilter))
|
|
1212
1273
|
|
|
1213
1274
|
// ββ Continuous ping loop β ping all models every N seconds forever ββββββββββ
|
|
1214
1275
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "free-coding-models",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.20",
|
|
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",
|