Added
- Custom providers (issue #197): any OpenAI-compatible endpoint can now be added to the config file under a new
customProviderssection and joins the live catalog on every surface: the TUI table, the provider filter, the Settings screen,--jsonoutput, the Web Dashboard, health probing with real latency/stability scoring, the Smart Router (routable like any built-in provider), and tool installs (OpenCode, Kilo, and every generic OpenAI-compatible writer).- Endpoints for Ollama, LM Studio, vLLM, llama.cpp or private gateways: define
urlplus amodelslist of[id, label?, tier?, sweScore?, ctx?]tuples; only the id is required, everything else gets sensible defaults (tier defaults toB). - Keys are optional: declare
apiKeyinline (with${VAR}placeholder support), point at an env var withenvVar, or omit both for keyless local servers, which stay visible without a key and remain routable. - Tool configs always get an env var reference: your declared
envVar, or a generatedFCM_CUSTOM_<NAME>when none is declared. - Safety rails: provider keys cannot collide with built-in providers (colliding entries are ignored), invalid entries are dropped without breaking the rest of the config, and the merge is idempotent so hand-edited definitions reach a running daemon on its next config reload.
- Endpoints for Ollama, LM Studio, vLLM, llama.cpp or private gateways: define
Fixed
- Router ignored environment-variable API keys: the daemon's key resolver only read the config file, so Docker setups feeding keys through
env_file(issue #196 style) probed fine in the TUI but were skipped by the router withmissing_key. The daemon now resolves keys through the same chain as every other surface: env var > configapiKeys(with${VAR}expansion) >customProvidersdeclared key. - Keyless providers were unroutable: Kilo, LLM7 and OVHcloud sandbox answer without a key, but the router and the probe loop rejected them with
missing_keybefore ever probing (default router sets could silently contain models the router refused to use). Keyless providers now probe and route; when no key exists, noAuthorizationheader is sent at all instead of a bogusBearer null.
Changed
- Dependabot now waits a 7-day cooldown before proposing freshly published npm and GitHub Actions versions (supply-chain hardening, flagged by semgrep), and chalk major updates are ignored until the package's minimum Node version reaches 22 (chalk 6 requires Node >= 22, FCM supports Node >= 18).
- New provider: Runcrate (5 free models with a GitHub account, PR #204 by @heydryft) - catalog now 25 providers / 257 models.
Tests
- 20 new tests covering custom provider normalization (validation, collisions, defaults), the idempotent catalog merge, metadata/env-var/auth-endpoint registration, the key resolution chain, and config persistence round-trips. Full suite: 1206 tests, all passing.