free-coding-models 0.1.48 โ 0.1.49
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 +3 -2
- package/bin/free-coding-models.js +32 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -61,7 +61,8 @@
|
|
|
61
61
|
- **๐ป OpenCode integration** โ Auto-detects NIM setup, sets model as default, launches OpenCode
|
|
62
62
|
- **๐ฆ OpenClaw integration** โ Sets selected model as default provider in `~/.openclaw/openclaw.json`
|
|
63
63
|
- **๐จ Clean output** โ Zero scrollback pollution, interface stays open until Ctrl+C
|
|
64
|
-
- **๐ถ Status indicators** โ UP โ
ยท Timeout โณ ยท Overloaded ๐ฅ ยท Not Found ๐ซ
|
|
64
|
+
- **๐ถ Status indicators** โ UP โ
ยท No Key ๐ ยท Timeout โณ ยท Overloaded ๐ฅ ยท Not Found ๐ซ
|
|
65
|
+
- **๐ Keyless latency** โ Models are pinged even without an API key โ a `๐ NO KEY` status confirms the server is reachable with real latency shown, so you can compare providers before committing to a key
|
|
65
66
|
- **๐ท Tier filtering** โ Filter models by tier letter (S, A, B, C) with `--tier` flag or dynamically with `T` key
|
|
66
67
|
|
|
67
68
|
---
|
|
@@ -78,7 +79,7 @@ Before using `free-coding-models`, make sure you have:
|
|
|
78
79
|
3. **OpenCode** *(optional)* โ [Install OpenCode](https://github.com/opencode-ai/opencode) to use the OpenCode integration
|
|
79
80
|
4. **OpenClaw** *(optional)* โ [Install OpenClaw](https://openclaw.ai) to use the OpenClaw integration
|
|
80
81
|
|
|
81
|
-
> ๐ก **Tip:** You don't need all three providers. One key is enough to get started. Add more later via the Settings screen (`P` key).
|
|
82
|
+
> ๐ก **Tip:** You don't need all three providers. One key is enough to get started. Add more later via the Settings screen (`P` key). Models without a key still show real latency (`๐ NO KEY`) so you can evaluate providers before signing up.
|
|
82
83
|
|
|
83
84
|
---
|
|
84
85
|
|
|
@@ -574,7 +574,7 @@ function renderTable(results, pendingPings, frame, cursor = null, sortColumn = '
|
|
|
574
574
|
: chalk.dim(ctxRaw.padEnd(W_CTX))
|
|
575
575
|
|
|
576
576
|
// ๐ Latest ping - pings are objects: { ms, code }
|
|
577
|
-
// ๐
|
|
577
|
+
// ๐ Show response time for 200 (success) and 401 (no-auth but server is reachable)
|
|
578
578
|
const latestPing = r.pings.length > 0 ? r.pings[r.pings.length - 1] : null
|
|
579
579
|
let pingCell
|
|
580
580
|
if (!latestPing) {
|
|
@@ -583,6 +583,9 @@ function renderTable(results, pendingPings, frame, cursor = null, sortColumn = '
|
|
|
583
583
|
// ๐ Success - show response time
|
|
584
584
|
const str = String(latestPing.ms).padEnd(W_PING)
|
|
585
585
|
pingCell = latestPing.ms < 500 ? chalk.greenBright(str) : latestPing.ms < 1500 ? chalk.yellow(str) : chalk.red(str)
|
|
586
|
+
} else if (latestPing.code === '401') {
|
|
587
|
+
// ๐ 401 = no API key but server IS reachable โ still show latency in dim
|
|
588
|
+
pingCell = chalk.dim(String(latestPing.ms).padEnd(W_PING))
|
|
586
589
|
} else {
|
|
587
590
|
// ๐ Error or timeout - show "โ" (error code is already in Status column)
|
|
588
591
|
pingCell = chalk.dim('โ'.padEnd(W_PING))
|
|
@@ -601,7 +604,11 @@ function renderTable(results, pendingPings, frame, cursor = null, sortColumn = '
|
|
|
601
604
|
// ๐ Status column - build plain text with emoji, pad, then colorize
|
|
602
605
|
// ๐ Different emojis for different error codes
|
|
603
606
|
let statusText, statusColor
|
|
604
|
-
if (r.status === '
|
|
607
|
+
if (r.status === 'noauth') {
|
|
608
|
+
// ๐ Server responded but needs an API key โ shown dimly since it IS reachable
|
|
609
|
+
statusText = `๐ NO KEY`
|
|
610
|
+
statusColor = (s) => chalk.dim(s)
|
|
611
|
+
} else if (r.status === 'pending') {
|
|
605
612
|
statusText = `${FRAMES[frame % FRAMES.length]} wait`
|
|
606
613
|
statusColor = (s) => chalk.dim.yellow(s)
|
|
607
614
|
} else if (r.status === 'up') {
|
|
@@ -718,14 +725,19 @@ function renderTable(results, pendingPings, frame, cursor = null, sortColumn = '
|
|
|
718
725
|
|
|
719
726
|
// ๐ ping: Send a single chat completion request to measure model availability and latency.
|
|
720
727
|
// ๐ url param is the provider's endpoint URL โ differs per provider (NIM, Groq, Cerebras).
|
|
728
|
+
// ๐ apiKey can be null โ in that case no Authorization header is sent.
|
|
729
|
+
// ๐ A 401 response still tells us the server is UP and gives us real latency.
|
|
721
730
|
async function ping(apiKey, modelId, url) {
|
|
722
731
|
const ctrl = new AbortController()
|
|
723
732
|
const timer = setTimeout(() => ctrl.abort(), PING_TIMEOUT)
|
|
724
733
|
const t0 = performance.now()
|
|
725
734
|
try {
|
|
735
|
+
// ๐ Only attach Authorization header when a key is available
|
|
736
|
+
const headers = { 'Content-Type': 'application/json' }
|
|
737
|
+
if (apiKey) headers['Authorization'] = `Bearer ${apiKey}`
|
|
726
738
|
const resp = await fetch(url, {
|
|
727
739
|
method: 'POST', signal: ctrl.signal,
|
|
728
|
-
headers
|
|
740
|
+
headers,
|
|
729
741
|
body: JSON.stringify({ model: modelId, messages: [{ role: 'user', content: 'hi' }], max_tokens: 1 }),
|
|
730
742
|
})
|
|
731
743
|
return { code: String(resp.status), ms: Math.round(performance.now() - t0) }
|
|
@@ -1524,6 +1536,16 @@ async function main() {
|
|
|
1524
1536
|
results.forEach((r, i) => { r.idx = i + 1 })
|
|
1525
1537
|
state.results = results
|
|
1526
1538
|
adjustScrollOffset(state)
|
|
1539
|
+
// ๐ Re-ping all models that were 'noauth' (got 401 without key) but now have a key
|
|
1540
|
+
// ๐ This makes the TUI react immediately when a user adds an API key in settings
|
|
1541
|
+
state.results.forEach(r => {
|
|
1542
|
+
if (r.status === 'noauth' && getApiKey(state.config, r.providerKey)) {
|
|
1543
|
+
r.status = 'pending'
|
|
1544
|
+
r.pings = []
|
|
1545
|
+
r.httpCode = null
|
|
1546
|
+
pingModel(r).catch(() => {})
|
|
1547
|
+
}
|
|
1548
|
+
})
|
|
1527
1549
|
return
|
|
1528
1550
|
}
|
|
1529
1551
|
|
|
@@ -1707,8 +1729,9 @@ async function main() {
|
|
|
1707
1729
|
|
|
1708
1730
|
// ๐ Single ping function that updates result
|
|
1709
1731
|
// ๐ Uses per-provider API key and URL from sources.js
|
|
1732
|
+
// ๐ If no API key is configured, pings without auth โ a 401 still tells us latency + server is up
|
|
1710
1733
|
const pingModel = async (r) => {
|
|
1711
|
-
const providerApiKey = getApiKey(state.config, r.providerKey) ??
|
|
1734
|
+
const providerApiKey = getApiKey(state.config, r.providerKey) ?? null
|
|
1712
1735
|
const providerUrl = sources[r.providerKey]?.url ?? sources.nvidia.url
|
|
1713
1736
|
const { code, ms } = await ping(providerApiKey, r.modelId, providerUrl)
|
|
1714
1737
|
|
|
@@ -1722,6 +1745,11 @@ async function main() {
|
|
|
1722
1745
|
r.status = 'up'
|
|
1723
1746
|
} else if (code === '000') {
|
|
1724
1747
|
r.status = 'timeout'
|
|
1748
|
+
} else if (code === '401') {
|
|
1749
|
+
// ๐ 401 = server is reachable but no API key set (or wrong key)
|
|
1750
|
+
// ๐ Treated as 'noauth' โ server is UP, latency is real, just needs a key
|
|
1751
|
+
r.status = 'noauth'
|
|
1752
|
+
r.httpCode = code
|
|
1725
1753
|
} else {
|
|
1726
1754
|
r.status = 'down'
|
|
1727
1755
|
r.httpCode = code
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "free-coding-models",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.49",
|
|
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",
|