free-coding-models 0.3.25 → 0.3.28

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
@@ -1,11 +1,71 @@
1
1
  # Changelog
2
2
  ---
3
3
 
4
+ ## [0.3.27] - 2026-03-27
5
+
6
+ ### Added
7
+ - **Fluorescent green UPDATE AVAILABLE banner** — impossible-to-miss fluo green (🚀⬆️) banner at the bottom of the TUI when a new version is detected; click it to update instantly
8
+ - **Command Palette update entry** — `⬆️ UPDATE NOW` is always the first result in Ctrl+P when an update is available
9
+ - **Shift+U hotkey** — press Shift+U from the main table to trigger an immediate update
10
+ - **Mouse-clickable update banner** — click the fluo green banner to install the latest version and relaunch
11
+ - **Background version re-check every 5 minutes** — if a new version is published while the TUI is open, the banner appears live without restarting
12
+ - **Aggressive pre-TUI update prompt** — fluorescent green header, and "Continue without update" warns that reminders will follow
13
+ - **Last release date in footer** — light pink `Last release: Mar 27, 2026, 09:42 PM` shows when the package was last published to npm, so users know how fresh the model data is
14
+
15
+ ### Fixed
16
+ - **Auto-update now detects the correct package manager** — bun, pnpm, and yarn users no longer get duplicate npm installs (fixes #46)
17
+ - Update banner in footer shows the correct install command for your package manager
18
+
19
+ ## [0.3.26] - 2026-03-27
20
+
21
+ ### Added
22
+ - **Groq**: Added Compound + Compound Mini; fixed Llama 4 Scout context (10M)
23
+ - **OpenRouter**: Added MiniMax M2.5, Nemotron 3 Super, Hermes 3 405B, Gemma 3n E4B
24
+ - **HuggingFace**: Replaced invalid DeepSeek-V3-Coder + outdated starcoder2-15b with DeepSeek V3 0324 + Qwen2.5 Coder 32B
25
+ - **Replicate**: Replaced CodeLlama 70B (2023) with DeepSeek V3 0324 + Llama 3.3 70B
26
+ - **Cloudflare**: Added Kimi K2.5, GLM-4.7-Flash, Llama 4 Scout, Nemotron 3 Super, Qwen3 30B MoE
27
+ - **Scaleway**: Added Qwen3.5 400B VLM + Mistral Large 675B
28
+ - **DeepInfra**: Replaced Mixtral Code with Nemotron 3 Super + DeepSeek V3 0324 + Qwen3 235B
29
+ - **Fireworks**: Added Llama 4 Maverick + Qwen3 235B
30
+ - **Hyperbolic**: Added Qwen3 80B Thinking variant
31
+ - **Together AI**: Added Qwen3.5 400B VLM, MiniMax M2.5, GLM-5
32
+
33
+ ### Changed
34
+ - **Rovo Dev CLI**: Updated Sonnet 4 → Sonnet 4.6, added Opus 4.6
35
+ - **Groq**: Removed 4 deprecated models (R1 Distill 70B, QwQ 32B, Kimi K2, Maverick)
36
+ - **OpenRouter**: Updated context sizes for multiple models
37
+
4
38
  ## [0.3.25] - 2026-03-19
5
39
 
40
+ ### Added
41
+ - **Installed Models Manager** — View, launch, and disable models configured in external tools (Goose, Crush, Aider, Qwen, Pi, OpenHands, Amp)
42
+ - Access via Command Palette (Ctrl+P) → "Installed models"
43
+ - Scans all supported tool configs automatically on opening
44
+ - Displays all models per tool (e.g., Crush shows both large and small models)
45
+ - Actions: Launch (Enter), Disable (D) with backup, Reinstall (R)
46
+ - Soft delete: Comments out model entries and saves backups to ~/.free-coding-models-backups.json
47
+ - **Full mouse support for the TUI** — Click, right-click, double-click, and scroll work throughout the interface
48
+ - **Click column headers** — Sort by any column (click Rank, Tier, SWE%, CTX, Model, Provider, etc.)
49
+ - **Click model rows** — Move cursor to any model (left-click)
50
+ - **Right-click model rows** — Toggle favorite (same as F key)
51
+ - **Double-click model rows** — Select model and launch (same as Enter)
52
+ - **Mouse wheel** — Scroll through the main table, overlays (Settings, Help, Changelog), and command palette results
53
+ - **Click CLI Tools header** — Cycle through tool modes (same as Z key)
54
+ - **Click Tier header** — Cycle through tier filters (same as T key)
55
+ - **Click footer hotkeys** — Trigger any visible hotkey from the footer
56
+ - **Command palette click** — Click inside to select items, double-click to confirm; click outside to close
57
+ - **Recommend questionnaire click** — Click on option rows to select, double-click to confirm
58
+ - **Mouse unit tests** — 46 new tests covering SGR sequence parsing, double-click detection, modifiers, and COLUMN_SORT_MAP validation
59
+
6
60
  ### Changed
7
- - **Removed "CLI Tools" column** — The compat emoji column has been removed from the TUI table, freeing ~22 characters of horizontal space for other columns
8
- - **Cleaner table layout** — Responsive column hiding no longer needs to drop the compat column first on narrow terminals
61
+ - **CLI Tools column redesigned** — Renamed from "Compatible with" to "CLI Tools", with left-aligned emoji display (compatible tools packed left instead of fixed slot positions)
62
+ - **Sort arrow overflow fixed** — SWE%, CTX, Stability, and Uptime columns now properly fit within their widths when sorted (arrow now `↑SWE%` instead of `↑ SWE%`)
63
+ - **Mouse sequence suppression** — SGR mouse sequences no longer leak into keypress handlers (prevents spurious sort/filter triggers when clicking)
64
+
65
+ ### Fixed
66
+ - **Command palette scroll leak** — Mouse wheel no longer injects raw SGR sequence bytes into the command palette text input
67
+ - **Double-action on model click** — Clicking a model row now only moves the cursor; it no longer simultaneously triggers column sorting
68
+ - **Mouse event listener order** — Fixed race condition where readline emitted keypress events before mouse data was processed
9
69
 
10
70
  ## [0.3.24] - 2026-03-19
11
71
 
package/README.md CHANGED
@@ -229,6 +229,8 @@ When a tool mode is active (via `Z`), models incompatible with that tool are hig
229
229
 
230
230
  ## ⌨️ TUI Keys
231
231
 
232
+ ### Keyboard
233
+
232
234
  | Key | Action |
233
235
  |-----|--------|
234
236
  | `↑↓` | Navigate models |
@@ -243,6 +245,7 @@ When a tool mode is active (via `Z`), models incompatible with that tool are hig
243
245
  | `G` | Cycle global theme (`Auto → Dark → Light`) |
244
246
  | `Ctrl+P` | Open ⚡️ command palette (search + run actions) |
245
247
  | `R/S/C/M/O/L/A/H/V/B/U` | Sort columns |
248
+ | `Shift+U` | Update to latest version (when update available) |
246
249
  | `P` | Settings (API keys, providers, updates, theme) |
247
250
  | `Q` | Smart Recommend overlay |
248
251
  | `N` | Changelog |
@@ -251,6 +254,23 @@ When a tool mode is active (via `Z`), models incompatible with that tool are hig
251
254
  | `K` | Help overlay |
252
255
  | `Ctrl+C` | Exit |
253
256
 
257
+ ### Mouse
258
+
259
+ | Action | Result |
260
+ |--------|--------|
261
+ | **Click column header** | Sort by that column |
262
+ | **Click Tier header** | Cycle tier filter |
263
+ | **Click CLI Tools header** | Cycle tool mode |
264
+ | **Click model row** | Move cursor to model |
265
+ | **Double-click model row** | Select and launch model |
266
+ | **Right-click model row** | Toggle favorite |
267
+ | **Scroll wheel** | Navigate table / overlays / palette |
268
+ | **Click footer hotkey** | Trigger that action |
269
+ | **Click update banner** | Install latest version and relaunch |
270
+ | **Click command palette item** | Select item (double-click to confirm) |
271
+ | **Click recommend option** | Select option (double-click to confirm) |
272
+ | **Click outside modal** | Close command palette |
273
+
254
274
  → **[Stability score & column reference](./docs/stability.md)**
255
275
 
256
276
  ---
@@ -272,8 +292,10 @@ When a tool mode is active (via `Z`), models incompatible with that tool are hig
272
292
  - **OpenCode Zen models** — 8 free models exclusive to OpenCode CLI/Desktop, powered by the Zen AI gateway
273
293
  - **Width guardrail** — shows a warning instead of a broken table in narrow terminals
274
294
  - **Readable everywhere** — semantic theme palette keeps table rows, overlays, badges, and help screens legible in dark and light terminals
275
- - **Global theme switch** — `G` cycles `auto`, `dark`, and `light` live without restarting
295
+ - **Global theme switch** — `G` cycles `auto`, `dark`, + `light` live without restarting
276
296
  - **Auto-retry** — timeout models keep getting retried
297
+ - **Aggressive update nudging** — fluorescent green banner when an update is available, impossible to miss, Shift+U hotkey, command palette entry, background re-check every 5 min, mid-session updates the banner live without restarting
298
+ - **Last release timestamp** — light pink footer shows `Last release: Mar 27, 2026, 09:42 PM` from npm so users know how fresh the data is
277
299
 
278
300
  ---
279
301
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "free-coding-models",
3
- "version": "0.3.25",
3
+ "version": "0.3.28",
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/sources.js CHANGED
@@ -99,15 +99,13 @@ export const nvidiaNim = [
99
99
  // 📖 Free API keys available at https://console.groq.com/keys
100
100
  export const groq = [
101
101
  ['llama-3.3-70b-versatile', 'Llama 3.3 70B', 'A-', '39.5%', '128k'],
102
- ['meta-llama/llama-4-scout-17b-16e-preview', 'Llama 4 Scout', 'A', '44.0%', '10M'],
103
- ['meta-llama/llama-4-maverick-17b-128e-preview', 'Llama 4 Maverick', 'S', '62.0%', '1M'],
104
- ['deepseek-r1-distill-llama-70b', 'R1 Distill 70B', 'A', '43.9%', '128k'],
105
- ['qwen-qwq-32b', 'QwQ 32B', 'A+', '50.0%', '131k'],
106
- ['moonshotai/kimi-k2-instruct', 'Kimi K2 Instruct', 'S', '65.8%', '131k'],
102
+ ['meta-llama/llama-4-scout-17b-16e-preview', 'Llama 4 Scout', 'A', '44.0%', '131k'],
107
103
  ['llama-3.1-8b-instant', 'Llama 3.1 8B', 'B', '28.8%', '128k'],
108
104
  ['openai/gpt-oss-120b', 'GPT OSS 120B', 'S', '60.0%', '128k'],
109
105
  ['openai/gpt-oss-20b', 'GPT OSS 20B', 'A', '42.0%', '128k'],
110
106
  ['qwen/qwen3-32b', 'Qwen3 32B', 'A+', '50.0%', '131k'],
107
+ ['groq/compound', 'Groq Compound', 'A', '45.0%', '131k'],
108
+ ['groq/compound-mini', 'Groq Compound Mini', 'B+', '32.0%', '131k'],
111
109
  ]
112
110
 
113
111
  // 📖 Cerebras source - https://cloud.cerebras.ai
@@ -158,36 +156,43 @@ export const sambanova = [
158
156
  // 📖 API keys at https://openrouter.ai/keys
159
157
  export const openrouter = [
160
158
  ['qwen/qwen3-coder:free', 'Qwen3 Coder 480B', 'S+', '70.6%', '262k'],
161
- ['z-ai/glm-4.5-air:free', 'GLM 4.5 Air', 'S+', '72.0%', '128k'],
162
- ['google/gemma-3-27b-it:free', 'Gemma 3 27B', 'B', '22.0%', '128k'],
159
+ ['minimax/minimax-m2.5:free', 'MiniMax M2.5', 'S+', '74.0%', '197k'],
160
+ ['z-ai/glm-4.5-air:free', 'GLM 4.5 Air', 'S+', '72.0%', '131k'],
163
161
  ['stepfun/step-3.5-flash:free', 'Step 3.5 Flash', 'S+', '74.4%', '256k'],
164
- ['qwen/qwen3-next-80b-a3b-instruct:free', 'Qwen3 80B Instruct', 'S', '65.0%', '128k'],
165
- ['openai/gpt-oss-120b:free', 'GPT OSS 120B', 'S', '60.0%', '128k'],
166
- ['openai/gpt-oss-20b:free', 'GPT OSS 20B', 'A', '42.0%', '128k'],
162
+ ['nvidia/nemotron-3-super-120b-a12b:free', 'Nemotron 3 Super', 'A+', '56.0%', '262k'],
163
+ ['qwen/qwen3-next-80b-a3b-instruct:free', 'Qwen3 80B Instruct', 'S', '65.0%', '131k'],
164
+ ['nousresearch/hermes-3-llama-3.1-405b:free', 'Hermes 3 405B', 'A', '44.0%', '131k'],
165
+ ['openai/gpt-oss-120b:free', 'GPT OSS 120B', 'S', '60.0%', '131k'],
166
+ ['openai/gpt-oss-20b:free', 'GPT OSS 20B', 'A', '42.0%', '131k'],
167
167
  ['nvidia/nemotron-3-nano-30b-a3b:free', 'Nemotron Nano 30B', 'A', '43.0%', '128k'],
168
- ['meta-llama/llama-3.3-70b-instruct:free', 'Llama 3.3 70B', 'A-', '39.5%', '128k'],
168
+ ['meta-llama/llama-3.3-70b-instruct:free', 'Llama 3.3 70B', 'A-', '39.5%', '131k'],
169
169
  ['mistralai/mistral-small-3.1-24b-instruct:free', 'Mistral Small 3.1', 'B+', '30.0%', '128k'],
170
- ['google/gemma-3-12b-it:free', 'Gemma 3 12B', 'C', '15.0%', '128k'],
170
+ ['google/gemma-3-27b-it:free', 'Gemma 3 27B', 'B', '22.0%', '131k'],
171
+ ['google/gemma-3-12b-it:free', 'Gemma 3 12B', 'C', '15.0%', '131k'],
172
+ ['google/gemma-3n-e4b-it:free', 'Gemma 3n E4B', 'C', '10.0%', '8k'],
171
173
  ]
172
174
 
173
175
  // 📖 Hugging Face Inference source - https://huggingface.co
174
176
  // 📖 OpenAI-compatible endpoint via router.huggingface.co/v1
175
177
  // 📖 Free monthly credits on developer accounts (~$0.10) — token at https://huggingface.co/settings/tokens
176
178
  export const huggingface = [
177
- ['deepseek-ai/DeepSeek-V3-Coder', 'DeepSeek V3 Coder', 'S', '62.0%', '128k'],
178
- ['bigcode/starcoder2-15b', 'StarCoder2 15B', 'B', '25.0%', '16k'],
179
+ ['deepseek-ai/DeepSeek-V3-0324', 'DeepSeek V3 0324', 'S', '62.0%', '128k'],
180
+ ['Qwen/Qwen2.5-Coder-32B-Instruct', 'Qwen2.5 Coder 32B', 'A', '46.0%', '32k'],
179
181
  ]
180
182
 
181
183
  // 📖 Replicate source - https://replicate.com
182
184
  // 📖 Uses predictions endpoint (not OpenAI chat-completions) with token auth
183
185
  export const replicate = [
184
- ['codellama/CodeLlama-70b-Instruct-hf', 'CodeLlama 70B', 'A-', '39.0%', '16k'],
186
+ ['deepseek-ai/DeepSeek-V3-0324', 'DeepSeek V3 0324', 'S', '62.0%', '128k'],
187
+ ['meta/llama-3.3-70b-instruct', 'Llama 3.3 70B', 'A-', '39.5%', '128k'],
185
188
  ]
186
189
 
187
190
  // 📖 DeepInfra source - https://deepinfra.com
188
191
  // 📖 OpenAI-compatible endpoint: https://api.deepinfra.com/v1/openai/chat/completions
189
192
  export const deepinfra = [
190
- ['mistralai/Mixtral-8x22B-Instruct-v0.1', 'Mixtral Code', 'B+', '32.0%', '64k'],
193
+ ['nvidia/Nemotron-3-Super', 'Nemotron 3 Super', 'A+', '56.0%', '128k'],
194
+ ['deepseek-ai/DeepSeek-V3-0324', 'DeepSeek V3 0324', 'S', '62.0%', '128k'],
195
+ ['Qwen/Qwen3-235B-A22B', 'Qwen3 235B', 'S+', '70.0%', '128k'],
191
196
  ['meta-llama/Meta-Llama-3.1-70B-Instruct', 'Llama 3.1 70B', 'A-', '39.5%', '128k'],
192
197
  ]
193
198
 
@@ -197,6 +202,8 @@ export const deepinfra = [
197
202
  export const fireworks = [
198
203
  ['accounts/fireworks/models/deepseek-v3', 'DeepSeek V3', 'S', '62.0%', '128k'],
199
204
  ['accounts/fireworks/models/deepseek-r1', 'DeepSeek R1', 'S', '61.0%', '128k'],
205
+ ['accounts/fireworks/models/llama4-maverick-instruct-basic', 'Llama 4 Maverick', 'S', '62.0%', '1M'],
206
+ ['accounts/fireworks/models/qwen3-235b-a22b', 'Qwen3 235B', 'S+', '70.0%', '128k'],
200
207
  ]
201
208
 
202
209
  // 📖 Mistral Codestral source - https://codestral.mistral.ai
@@ -215,6 +222,7 @@ export const hyperbolic = [
215
222
  ['openai/gpt-oss-120b', 'GPT OSS 120B', 'S', '60.0%', '128k'],
216
223
  ['Qwen/Qwen3-235B-A22B', 'Qwen3 235B', 'S+', '70.0%', '128k'],
217
224
  ['qwen/qwen3-next-80b-a3b-instruct', 'Qwen3 80B Instruct', 'S', '65.0%', '128k'],
225
+ ['Qwen/Qwen3-Next-80B-A3B-Thinking', 'Qwen3 80B Thinking', 'S', '68.0%', '128k'],
218
226
  ['deepseek-ai/DeepSeek-V3-0324', 'DeepSeek V3 0324', 'S', '62.0%', '128k'],
219
227
  ['Qwen/Qwen2.5-Coder-32B-Instruct', 'Qwen2.5 Coder 32B', 'A', '46.0%', '32k'],
220
228
  ['meta-llama/Llama-3.3-70B-Instruct', 'Llama 3.3 70B', 'A-', '39.5%', '128k'],
@@ -224,9 +232,10 @@ export const hyperbolic = [
224
232
  // 📖 Scaleway source - https://console.scaleway.com
225
233
  // 📖 1M free tokens — API keys at https://console.scaleway.com/iam/api-keys
226
234
  export const scaleway = [
227
- ['devstral-2-123b-instruct-2512', 'Devstral 2 123B', 'S+', '72.2%', '256k'],
235
+ ['devstral-2-123b-instruct-2512', 'Devstral 2 123B', 'S+', '72.2%', '256k'],
236
+ ['qwen3.5-397b-a17b', 'Qwen3.5 400B VLM', 'S', '68.0%', '250k'],
237
+ ['mistral/mistral-large-3-675b-instruct-2512', 'Mistral Large 675B', 'A+', '58.0%', '250k'],
228
238
  ['qwen3-235b-a22b-instruct-2507', 'Qwen3 235B', 'S+', '70.0%', '128k'],
229
- ['gpt-oss-120b', 'GPT OSS 120B', 'S', '60.0%', '128k'],
230
239
  ['qwen3-coder-30b-a3b-instruct', 'Qwen3 Coder 30B', 'A+', '55.0%', '32k'],
231
240
  ['llama-3.3-70b-instruct', 'Llama 3.3 70B', 'A-', '39.5%', '128k'],
232
241
  ['deepseek-r1-distill-llama-70b', 'R1 Distill 70B', 'A', '43.9%', '128k'],
@@ -272,6 +281,9 @@ export const siliconflow = [
272
281
  // 📖 Credits/promotions vary by account and region; verify current quota in console.
273
282
  export const together = [
274
283
  ['moonshotai/Kimi-K2.5', 'Kimi K2.5', 'S+', '76.8%', '128k'],
284
+ ['Qwen/Qwen3.5-397B-A17B', 'Qwen3.5 400B VLM', 'S', '68.0%', '250k'],
285
+ ['MiniMaxAI/MiniMax-M2.5', 'MiniMax M2.5', 'S+', '80.2%', '200k'],
286
+ ['zai-org/GLM-5', 'GLM-5', 'S+', '77.8%', '128k'],
275
287
  ['Qwen/Qwen3-Coder-480B-A35B-Instruct-FP8', 'Qwen3 Coder 480B', 'S+', '70.6%', '256k'],
276
288
  ['deepseek-ai/DeepSeek-V3.1', 'DeepSeek V3.1', 'S', '62.0%', '128k'],
277
289
  ['deepseek-ai/DeepSeek-R1', 'DeepSeek R1', 'S', '61.0%', '128k'],
@@ -285,7 +297,12 @@ export const together = [
285
297
  // 📖 https://api.cloudflare.com/client/v4/accounts/{account_id}/ai/v1/chat/completions
286
298
  // 📖 Free plan includes daily neuron quota and provider-level request limits.
287
299
  export const cloudflare = [
300
+ ['@cf/moonshotai/kimi-k2.5', 'Kimi K2.5', 'S+', '76.8%', '256k'],
301
+ ['@cf/zhipu/glm-4.7-flash', 'GLM-4.7-Flash', 'S', '59.2%', '131k'],
288
302
  ['@cf/openai/gpt-oss-120b', 'GPT OSS 120B', 'S', '60.0%', '128k'],
303
+ ['@cf/meta/llama-4-scout-17b-16e-instruct', 'Llama 4 Scout', 'A', '44.0%', '131k'],
304
+ ['@cf/nvidia/nemotron-3-120b-a12b', 'Nemotron 3 Super', 'A+', '56.0%', '128k'],
305
+ ['@cf/qwen/qwen3-30b-a3b-fp8', 'Qwen3 30B MoE', 'A', '45.0%', '128k'],
289
306
  ['@cf/qwen/qwen2.5-coder-32b-instruct', 'Qwen2.5 Coder 32B', 'A', '46.0%', '32k'],
290
307
  ['@cf/deepseek-ai/deepseek-r1-distill-qwen-32b', 'R1 Distill 32B', 'A', '43.9%', '128k'],
291
308
  ['@cf/openai/gpt-oss-20b', 'GPT OSS 20B', 'A', '42.0%', '128k'],
@@ -350,7 +367,8 @@ export const iflow = [
350
367
  // 📖 Free tier: 5M tokens/day (beta) - Claude Sonnet 4 (72.7% SWE-bench)
351
368
  // 📖 Requires Atlassian account + Rovo Dev activated on your site
352
369
  export const rovo = [
353
- ['anthropic/claude-sonnet-4', 'Claude Sonnet 4 🆕', 'S+', '72.7%', '200k'],
370
+ ['anthropic/claude-sonnet-4.6', 'Claude Sonnet 4.6', 'S+', '75.0%', '200k'],
371
+ ['anthropic/claude-opus-4.6', 'Claude Opus 4.6', 'S+', '80.0%', '200k'],
354
372
  ]
355
373
 
356
374
  // 📖 Gemini CLI source - https://github.com/google-gemini/gemini-cli
package/src/app.js CHANGED
@@ -112,7 +112,7 @@ import { runFiableMode, filterByTierOrExit, fetchOpenRouterFreeModels } from '..
112
112
  import { PROVIDER_METADATA, ENV_VAR_NAMES, isWindows, isMac } from '../src/provider-metadata.js'
113
113
  import { parseTelemetryEnv, isTelemetryDebugEnabled, telemetryDebug, ensureTelemetryConfig, getTelemetryDistinctId, getTelemetrySystem, getTelemetryTerminal, isTelemetryEnabled, sendUsageTelemetry, sendBugReport } from '../src/telemetry.js'
114
114
  import { ensureFavoritesConfig, toFavoriteKey, syncFavoriteFlags, toggleFavoriteModel } from '../src/favorites.js'
115
- import { checkForUpdateDetailed, checkForUpdate, runUpdate, promptUpdateNotification } from '../src/updater.js'
115
+ import { checkForUpdateDetailed, checkForUpdate, runUpdate, promptUpdateNotification, fetchLastReleaseDate } from './updater.js'
116
116
  import { promptApiKey } from '../src/setup.js'
117
117
  import { stripAnsi, maskApiKey, displayWidth, padEndDisplay, tintOverlayLines, keepOverlayTargetVisible, sliceOverlayLines, calculateViewport, sortResultsWithPinnedFavorites, adjustScrollOffset } from '../src/render-helpers.js'
118
118
  import { renderTable, PROVIDER_COLOR } from '../src/render-table.js'
@@ -295,6 +295,8 @@ export async function runApp(cliArgs, config) {
295
295
  // 📖 Dynamic OpenRouter free model discovery — fetch live free models from API
296
296
  // 📖 Replaces static openrouter entries in MODELS with fresh data.
297
297
  // 📖 Fallback: if fetch fails, the static list from sources.js stays intact + warning shown.
298
+ const lastReleaseDate = await fetchLastReleaseDate()
299
+ state.lastReleaseDate = lastReleaseDate
298
300
  const dynamicModels = await fetchOpenRouterFreeModels()
299
301
  if (dynamicModels) {
300
302
  // 📖 Remove all existing openrouter entries from MODELS
@@ -381,6 +383,7 @@ export async function runApp(cliArgs, config) {
381
383
  lastUserActivityAt: now, // 📖 Any keypress refreshes this timer; inactivity can force slow mode.
382
384
  resumeSpeedOnActivity: false, // 📖 Set after idle slowdown so the next activity restarts a 60s speed burst.
383
385
  startupLatestVersion: latestVersion, // 📖 Startup auto-check result reused by the footer banner after "skip update".
386
+ lastReleaseDate: null, // 📖 Human-readable last npm publish date (fetched asynchronously).
384
387
  versionAlertsEnabled: !isDevMode, // 📖 Dev checkouts should not tell contributors to upgrade the global npm package.
385
388
  mode, // 📖 'opencode' or 'openclaw' — controls Enter action
386
389
  tierFilterMode: 0, // 📖 Index into TIER_CYCLE (0=All, 1=S+, 2=S, ...)
@@ -471,6 +474,12 @@ export async function runApp(cliArgs, config) {
471
474
  changelogPhase: 'index', // 📖 'index' (all versions) | 'details' (specific version)
472
475
  changelogCursor: 0, // 📖 Selected row in index phase
473
476
  changelogSelectedVersion: null, // 📖 Which version to show details for
477
+ // 📖 Installed Models overlay state (Command Palette → Installed models)
478
+ installedModelsOpen: false, // 📖 Whether the installed models overlay is active
479
+ installedModelsCursor: 0, // 📖 Selected row (tool or model)
480
+ installedModelsScrollOffset: 0, // 📖 Vertical scroll offset for overlay viewport
481
+ installedModelsData: [], // 📖 Cached scan results
482
+ installedModelsErrorMsg: null, // 📖 Error or status message
474
483
  // 📖 Custom text filter (Ctrl+P palette → type text → Enter). Ephemeral — not saved to config.
475
484
  customTextFilter: null, // 📖 Active free-text filter string (null = off). Matches model name, ctx, provider key/name.
476
485
  }
@@ -667,10 +676,10 @@ export async function runApp(cliArgs, config) {
667
676
 
668
677
  // 📖 Ensure we always leave alt screen cleanly (Ctrl+C, crash, normal exit)
669
678
  const exit = (code = 0) => {
670
- // 📖 Save cache before exiting so next run starts faster
671
679
  saveCache(state.results, state.pingMode)
672
680
  clearInterval(ticker)
673
681
  clearTimeout(state.pingIntervalObj)
682
+ clearInterval(state.versionRecheckTimer)
674
683
  process.stdout.write(ALT_LEAVE)
675
684
  if (process.stdout.isTTY) {
676
685
  process.stdout.flush && process.stdout.flush()
@@ -737,6 +746,7 @@ export async function runApp(cliArgs, config) {
737
746
  const stopUi = ({ resetRawMode = false } = {}) => {
738
747
  if (ticker) clearInterval(ticker)
739
748
  clearTimeout(state.pingIntervalObj)
749
+ clearInterval(state.versionRecheckTimer)
740
750
  if (onKeyPress) process.stdin.removeListener('keypress', onKeyPress)
741
751
  if (onMouseData) process.stdin.removeListener('data', onMouseData)
742
752
  if (process.stdin.isTTY && resetRawMode) process.stdin.setRawMode(false)
@@ -951,7 +961,7 @@ export async function runApp(cliArgs, config) {
951
961
  refreshAutoPingMode()
952
962
  state.frame++
953
963
  // 📖 Cache visible+sorted models each frame so Enter handler always matches the display
954
- if (!state.settingsOpen && !state.installEndpointsOpen && !state.toolInstallPromptOpen && !state.incompatibleFallbackOpen && !state.recommendOpen && !state.feedbackOpen && !state.changelogOpen && !state.commandPaletteOpen) {
964
+ if (!state.settingsOpen && !state.installEndpointsOpen && !state.toolInstallPromptOpen && !state.incompatibleFallbackOpen && !state.recommendOpen && !state.feedbackOpen && !state.changelogOpen && !state.installedModelsOpen && !state.commandPaletteOpen) {
955
965
  const visible = state.results.filter(r => !r.hidden)
956
966
  state.visibleSorted = sortResultsWithPinnedFavorites(visible, state.sortColumn, state.sortDirection, {
957
967
  pinFavorites: state.favoritesPinnedAndSticky,
@@ -990,7 +1000,8 @@ export async function runApp(cliArgs, config) {
990
1000
  state.startupLatestVersion,
991
1001
  state.versionAlertsEnabled,
992
1002
  state.favoritesPinnedAndSticky,
993
- state.customTextFilter
1003
+ state.customTextFilter,
1004
+ state.lastReleaseDate
994
1005
  )
995
1006
  }
996
1007
  tableContent = state.commandPaletteFrozenTable
@@ -1024,7 +1035,8 @@ export async function runApp(cliArgs, config) {
1024
1035
  state.startupLatestVersion,
1025
1036
  state.versionAlertsEnabled,
1026
1037
  state.favoritesPinnedAndSticky,
1027
- state.customTextFilter
1038
+ state.customTextFilter,
1039
+ state.lastReleaseDate
1028
1040
  )
1029
1041
  }
1030
1042
 
@@ -1034,6 +1046,8 @@ export async function runApp(cliArgs, config) {
1034
1046
  ? overlays.renderInstallEndpoints()
1035
1047
  : state.toolInstallPromptOpen
1036
1048
  ? overlays.renderToolInstallPrompt()
1049
+ : state.installedModelsOpen
1050
+ ? overlays.renderInstalledModels()
1037
1051
  : state.incompatibleFallbackOpen
1038
1052
  ? overlays.renderIncompatibleFallback()
1039
1053
  : state.commandPaletteOpen
@@ -1066,7 +1080,7 @@ export async function runApp(cliArgs, config) {
1066
1080
  pinFavorites: state.favoritesPinnedAndSticky,
1067
1081
  })
1068
1082
 
1069
- 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, null, state.pingMode, state.pingModeSource, state.hideUnconfiguredModels, state.widthWarningStartedAt, state.widthWarningDismissed, state.widthWarningShowCount, state.settingsUpdateState, state.settingsUpdateLatestVersion, false, state.startupLatestVersion, state.versionAlertsEnabled, state.favoritesPinnedAndSticky, state.customTextFilter))
1083
+ 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, null, state.pingMode, state.pingModeSource, state.hideUnconfiguredModels, state.widthWarningStartedAt, state.widthWarningDismissed, state.widthWarningShowCount, state.settingsUpdateState, state.settingsUpdateLatestVersion, false, state.startupLatestVersion, state.versionAlertsEnabled, state.favoritesPinnedAndSticky, state.customTextFilter, state.lastReleaseDate))
1070
1084
  if (process.stdout.isTTY) {
1071
1085
  process.stdout.flush && process.stdout.flush()
1072
1086
  }
@@ -1141,6 +1155,19 @@ export async function runApp(cliArgs, config) {
1141
1155
  // 📖 Save cache after initial pings complete for faster next startup
1142
1156
  saveCache(state.results, state.pingMode)
1143
1157
 
1158
+ // 📖 Background version re-check: poll npm registry every 5 minutes.
1159
+ // 📖 If a new version appears (wasn't there at startup), update the banner live.
1160
+ const VERSION_RECHECK_INTERVAL_MS = 5 * 60 * 1000
1161
+ state.versionRecheckTimer = setInterval(async () => {
1162
+ if (isDevMode || !state.versionAlertsEnabled) return
1163
+ try {
1164
+ const fresh = await checkForUpdate()
1165
+ if (fresh) {
1166
+ state.startupLatestVersion = fresh
1167
+ }
1168
+ } catch {}
1169
+ }, VERSION_RECHECK_INTERVAL_MS)
1170
+
1144
1171
  // 📖 Keep interface running forever - user can select anytime or Ctrl+C to exit
1145
1172
  // 📖 The pings continue running in background with dynamic interval
1146
1173
  // 📖 User can press W to decrease interval (faster pings) or = to increase (slower)
@@ -203,6 +203,7 @@ const BASE_COMMAND_TREE = [
203
203
  { id: 'open-feedback', label: 'Feedback', shortcut: 'I', icon: '📝', type: 'page', description: 'Report bugs or requests', keywords: ['feedback', 'bug', 'request'] },
204
204
  { id: 'open-recommend', label: 'Smart recommend', shortcut: 'Q', icon: '🎯', type: 'page', description: 'Find best model for task', keywords: ['recommend', 'best model'] },
205
205
  { id: 'open-install-endpoints', label: 'Install endpoints', icon: '🔌', type: 'page', description: 'Install provider catalogs', keywords: ['install', 'endpoints', 'providers'] },
206
+ { id: 'open-installed-models', label: 'Installed models', icon: '🗂️', type: 'page', description: 'View models configured in tools', keywords: ['installed', 'models', 'configured', 'tools', 'manager', 'goose', 'crush', 'aider'] },
206
207
  ]
207
208
 
208
209
  /**