free-coding-models 0.5.11 → 0.5.12

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 CHANGED
@@ -543,9 +543,12 @@ When a tool mode is active (via `Z`), models incompatible with that tool are hig
543
543
  - **Favorites** — star models with `F`, persisted across sessions, default to normal rows, and switch display mode with `Y` (pinned+sticky vs normal rows)
544
544
  - **Configured-only default** — only shows providers you have keys for
545
545
  - **Keyless latency** — models ping even without an API key (show 🔑 NO KEY)
546
+ - **Unusable row fade** — rows in `NO KEY` or `AUTH FAIL` state are rendered at 80% opacity (20% less opaque) on every surface (TUI + Web + Desktop), so the user can scan the table and instantly see which models they cannot actually use. Composes cleanly with the favorite/recommended/incompatible background tints.
546
547
  - **Smart Recommend** — questionnaire picks the best model for your task type
547
548
  - **Smart Model Router** — local OpenAI-compatible daemon with model sets, failover, circuit breakers, health probes, and token stats
548
549
  - **Playground chat** — multi-turn chat with the router on every surface (TUI `;` / Web Playground nav / `free-coding-models --playground`). Streams responses and shows the routed-via provider/model on every reply.
550
+ - **Auto-heal on startup** — the daemon replaces broken models in the active set (`AUTH_ERROR` / `STALE`) with working alternatives from the same provider first, then cross-provider. The first manual edit disables auto-heal so user choices are preserved. A new user with a half-broken key set lands on a usable default set by the time the dashboard renders.
551
+ - **Web router set manager** — add, remove, drag-and-drop, and probe-sync the active router set from inside the Web Dashboard. The "Sync best models" button re-pings every candidate with the user's actual API keys and rebuilds the set with only models that return 2xx, so a new user lands on a working default set instead of a hardcoded one that 401s.
549
552
  - **Router pre-prompt** — a configurable first-class system message injected by the daemon on every `/v1/chat/completions` request it proxies. Default persona introduces the assistant as the FCM routing agent; editable from any surface.
550
553
  - **⚡️ Command Palette** — `Ctrl+P` opens a searchable action launcher for filters, sorting, overlays, and quick toggles
551
554
  - **Install Endpoints** — push a full provider catalog into any tool's config (from Settings `P` or ⚡️ Command Palette)
@@ -0,0 +1,152 @@
1
+ # Changelog v0.5.12 - 2026-06-02
2
+
3
+ ### Added
4
+ - **Web Router Dashboard: full set manager.** The M4 read-only "Model Health"
5
+ panel has been replaced with an interactive **Active Set** manager:
6
+ - **Add model** picker with search + per-provider filter (147 routeable
7
+ models from the new `/api/router/catalog` endpoint).
8
+ - **Remove** button per row.
9
+ - **Move up / Move down** buttons as a keyboard-accessible fallback.
10
+ - **HTML5 drag-and-drop** to reorder priorities. Visual drop indicator
11
+ (top/bottom border on the target row) shows where the row will land.
12
+ - **Active set switcher** in the same header (when more than one set
13
+ exists). Switches the daemon's `activeSet` via
14
+ `POST /sets/:name/activate`.
15
+ - **Save indicator** (`saving… / ✓ saved / ⚠ error`) shown next to the
16
+ Add button so the user always knows whether the last drag actually
17
+ persisted to disk.
18
+ - **"Sync best models" button.** One-click rebuild of the active set
19
+ using the probe-based `sync-set` pipeline. Re-pings every candidate
20
+ with the user's actual API keys, picks only the models that come back
21
+ 2xx with a reasonable latency, and persists the result. The Web UI
22
+ surfaces a "Synced N working models from M probes" toast with the
23
+ probe count. The button shows a "saving…" state while the probe runs
24
+ (up to ~60s for 16 candidates).
25
+ - **Daemon endpoints for granular set management.**
26
+ - `POST /sets/:name/models` — append a single model to a set.
27
+ - `DELETE /sets/:name/models` — remove a single model by
28
+ `{ provider, model }`.
29
+ - `POST /sets/:name/reorder` — accept a full priority order
30
+ `{ order: ["provider/model", ...] }` and re-number the set.
31
+ - `POST /sets/:name/sync` — re-run `sync-set` against the named set
32
+ with `maxProbes: 16, targetCount: 5` so the Web UI never spends
33
+ more than ~60s on a sync.
34
+ - `GET /api/router/catalog` — lightweight catalog of routeable
35
+ models for the Add picker.
36
+ - **Default router set is now probe-driven.** `buildDefaultRouterSet`
37
+ is async and accepts a `probeFn` that POSTs a 1-token chat completion
38
+ to each candidate. Models that come back 2xx with low latency are
39
+ pinned to the top of the set; failing models fall back to the static
40
+ tier ordering. A new user with a half-broken key set now gets a
41
+ default router set made of models that actually work, instead of a
42
+ hardcoded NVIDIA-only list that 401s for everyone without a
43
+ NIM-specific grant.
44
+ - **Web proxy for the new endpoints.** `/api/router/sets/:name/models`
45
+ (POST/DELETE), `/api/router/sets/:name/reorder` (POST),
46
+ `/api/router/sets/:name/activate` (POST), `/api/router/sets/:name/sync`
47
+ (POST, 180s proxy timeout), `/api/router/catalog` (GET, with a
48
+ fallback path that synthesizes the catalog from `sources.js` when the
49
+ daemon is offline).
50
+ - **Switch case routing fix.** The `if/regex` blocks for parameterized
51
+ routes (`/api/router/sets/:name/...`) were previously inside the
52
+ request switch as `case` statements that never matched (JS switch
53
+ is exact-string equality). Moved them above the switch so they
54
+ actually run. This is why the original reorder/add/remove proxies
55
+ returned 404 in the v0.5.11 web build.
56
+ - **Auto-heal the active router set on startup.** When the daemon starts,
57
+ it waits for the first probe burst to populate health data, then runs
58
+ `autoHealActiveSet()` to swap any broken model in the active set
59
+ (`AUTH_ERROR` or `STALE` state) for a working alternative. The picker
60
+ prefers the same provider first, then falls through to cross-provider
61
+ candidates, and skips providers whose every probe has been broken.
62
+ Three passes run at 8s, 24s, and 40s after startup so a freshly-added
63
+ replacement that turns out to be broken gets replaced again. The result:
64
+ a new user with a half-broken key set lands on a usable default set
65
+ by the time the Web Dashboard renders, and the Playground's `fcm`
66
+ virtual model starts returning successful responses immediately.
67
+ - **`router.userCustomized` + `router.autoHeal` config flags.** The first
68
+ manual edit to the active set (add / remove / reorder / sync /
69
+ activate / rename) flips `userCustomized` to `true` and `autoHeal`
70
+ to `false` so the user's manual choices are never undone on the next
71
+ daemon start. New users get `userCustomized: false` and `autoHeal:
72
+ true` by default, which is what powers the M6 "default to working
73
+ models" promise.
74
+ - **`/api/router/status` exposes `autoHeal`, `userCustomized`, and
75
+ `brokenModelCount`.** The Web Router Dashboard reads these to surface
76
+ an amber banner when broken models remain in the active set, with a
77
+ one-click "Fix now" button that re-runs the same `sync-set` probe
78
+ pipeline the CLI uses.
79
+ - **Amber "models not responding" banner in the Web Router Dashboard.**
80
+ Shown when the daemon reports `brokenModelCount > 0`, with a
81
+ dismissable X so the user can ignore it after acknowledging the
82
+ problem. The "Fix now" button re-runs the probe-based heal and reloads
83
+ the dashboard state.
84
+ - **Unusable row fade (TUI + Web + Desktop).** Rows whose health is
85
+ `NO KEY` (`noauth`) or `AUTH FAIL` (`auth_error`) are now rendered at
86
+ 80% opacity (20% less opaque) on every user-facing surface. The user
87
+ can scan the table and instantly see which models they cannot
88
+ actually use, even when the cursor is parked on a different model.
89
+ - **TUI:** the new `fadedRow()` helper in `src/tui/render-helpers.js`
90
+ multiplies every 24-bit RGB channel inside an ANSI-colored string by
91
+ `0.8`, so the whole line reads as uniformly faded. This works on
92
+ every terminal that supports truecolor and does not rely on the
93
+ SGR 2 "faint" code, which is ignored by some terminals. The fade
94
+ composes cleanly with the cursor highlight, the dark-red
95
+ `incompatible` background, the green `recommended` background, and
96
+ the gold `favorite` background, so no existing visual cue is lost
97
+ — the "unusable" signal just rides on top of them.
98
+ - **Web / Desktop (Tauri):** the `ModelTable` adds an
99
+ `.unusable { opacity: 0.8 }` CSS class to rows whose `m.status` is
100
+ `noauth` or `auth_error`. The class is held steady on hover so the
101
+ "you cannot use this" signal never disappears while the user is
102
+ inspecting the row.
103
+ - **`fadedRow(input, factor = 0.8)` helper.** Pure function exported
104
+ from `src/tui/render-helpers.js`, documented and unit-tested in
105
+ isolation. Identity fast-path for `factor >= 1`, channels clamped to
106
+ 0–255, bold/dim/reset SGR codes pass through unchanged. Reusable for
107
+ any future "fade a whole line" need (e.g. stale rows, soft-disabled
108
+ providers).
109
+ - **6 new unit tests** for the granular set-management endpoints
110
+ (add, duplicate, remove, reorder, reorder-with-missing-key,
111
+ catalog), **2 new tests** for `buildDefaultRouterSet`'s probe path
112
+ (probe-preference + sync fallback), **4 new unit tests** for the
113
+ auto-heal path (no-op when user-customized, no-op when auto-heal is
114
+ disabled, no-op when no broken models, and the
115
+ user-edit-flags-customization round-trip), and **12 new tests** for
116
+ the unusable row fade (7 for `fadedRow` + 5 for the renderTable
117
+ integration). **All 515 tests pass.**
118
+
119
+ ### Notes
120
+ - The TUI's `--sync-set` flag is unchanged — the Web "Sync best" button
121
+ is the same pipeline with a smaller `maxProbes` so the UI stays
122
+ snappy. CLI users still get the full 50-probe budget.
123
+ - The `fcm` virtual model that the Playground uses by default
124
+ automatically picks up the new working-models set, so the
125
+ Playground will start returning successful responses as soon as the
126
+ Web user clicks "Sync best" once.
127
+ - The auto-heal is best-effort: if the user has only one working
128
+ provider, the healed set will shrink to that one provider's top
129
+ model. That's still better than the previous behavior of showing
130
+ three models that all 401.
131
+ - The new broken-model banner is intentionally subtle (amber, not red)
132
+ because the auto-heal already does its best to recover. It's there
133
+ for the "user has 0 working keys" edge case so the user can click
134
+ through to "Fix now" / "Sync best" and either get a working set or
135
+ see the toast explaining the situation.
136
+ - The cursor row is still faded if the model is unusable — the user's
137
+ request was "the WHOLE line at 80% opacity" and we honor that
138
+ literally. The cursor highlight (blue background) gets its colors
139
+ multiplied by 0.8 too, so it remains visible but reads as "dimmed",
140
+ consistent with the rest of the line.
141
+
142
+ ### How the auto-heal picker works (M6)
143
+ ```
144
+ For each broken model in the active set:
145
+ 1. Same provider — pick a working model of the same provider
146
+ (skipping models that the circuit breaker already knows are broken).
147
+ 2. Cross-provider — fall through to any working model across all
148
+ providers, sorted by static tier + swe score.
149
+ 3. If neither yields a working model, leave the broken entry in
150
+ place and log a warning (the Web UI surfaces this in the
151
+ "models not responding" banner).
152
+ ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "free-coding-models",
3
- "version": "0.5.11",
3
+ "version": "0.5.12",
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",
@@ -167,6 +167,12 @@ export const DEFAULT_ROUTER_SETTINGS = Object.freeze({
167
167
  priorityWeight: 0.2,
168
168
  }),
169
169
  logLevel: 'info',
170
+ // 📖 autoHeal — when true (default), the daemon replaces broken models
171
+ // 📖 in the active set on startup (AUTH_ERROR / TIMEOUT / 5xx) with
172
+ // 📖 working alternatives from the same provider. Disabled the moment
173
+ // 📖 the user manually edits the set (reorder/add/remove), so the
174
+ // 📖 user always has the final say.
175
+ autoHeal: true,
170
176
  // 📖 Default pre-prompt injected as the first system message on every
171
177
  // 📖 /v1/chat/completions request the router proxies. Frames the assistant
172
178
  // 📖 as the FCM routing agent. Users can disable it or replace it from the
@@ -395,6 +401,15 @@ export function normalizeRouterConfig(router) {
395
401
  scoring: normalizeRouterScoring(router.scoring),
396
402
  logLevel,
397
403
  prePrompt: normalizeRouterPrePrompt(router.prePrompt),
404
+ // 📖 autoHeal defaults to true. Once the user explicitly edits a set
405
+ // 📖 (reorder/add/remove/sync) the daemon flips this to false so the
406
+ // 📖 user's manual choices are not undone on the next start.
407
+ autoHeal: router.autoHeal !== false,
408
+ // 📖 userCustomized marks the active set as "edited by the user".
409
+ // 📖 It is set the first time any write to the active set goes
410
+ // 📖 through and disables auto-heal for that set. Cleared if the
411
+ // 📖 user explicitly resets the set.
412
+ userCustomized: router.userCustomized === true,
398
413
  }
399
414
  }
400
415