Skip to content

v0.3.67

Choose a tag to compare

@github-actions github-actions released this 17 May 18:21
· 375 commits to main since this release

Added

  • 🐳 Docker Packaging: First-class Docker support so you can run FCM without installing Node.js. The official image is published to ghcr.io/vava-nessa/free-coding-models on every release and tag push.
    • Multi-arch friendly Dockerfile based on node:20-alpine, running as a non-root fcm user.
    • docker-entrypoint.sh auto-generates ~/.free-coding-models.json from any *_API_KEY / *_API_TOKEN env vars you pass to the container — no manual config step.
    • docker-compose.yml template wired up for every supported provider.
    • GitHub Actions workflow (.github/workflows/docker.yml) handles build + publish to GHCR on release: published, push: v*.*.* tags, and workflow_dispatch (with a test_mode dry-run input).
    • Trivy vulnerability scan blocks releases that introduce any CRITICAL/HIGH CVEs.
    • Quick start: docker run -p 19280:19280 -e OPENROUTER_API_KEY=... ghcr.io/vava-nessa/free-coding-models:latest.
  • 🌐 Combined Daemon + Web Dashboard: The router daemon now serves the web dashboard from the same port — no more juggling two processes. New REST surface area baked into the daemon:
    • GET /api/models — full model catalog with latency stats, status, p95, jitter, stability, verdict, uptime, and inRouterSet flag.
    • GET /api/config — provider catalog with masked API keys (••••••••XXXX) and enabled state.
    • GET /api/events — SSE stream the dashboard subscribes to for live updates.
    • GET /api/key/<provider> — reveal the raw API key for a configured provider (same-origin only).
    • POST /api/settings — save API keys and per-provider enabled flags from the dashboard, then trigger a probe burst.
    • When you add a new provider's API key from the dashboard, FCM now mirrors --sync-set behavior and automatically adds that provider's best-tier model to your active router set.

Changed

  • --web flag removed: replaced by --daemon, which now serves both the OpenAI-compatible router API and the dashboard on the same port. Existing tooling using --web should switch to --daemon.
  • Preserve user-created router sets on daemon start: previously the daemon rebuilt the active set from favorites or defaults on every restart, silently overwriting sets created with --sync-set. Named sets are now preserved.
  • Faster config reload: CONFIG_RELOAD_INTERVAL_MS shortened from 60s → 10s so dashboard-driven changes (toggling providers, adding keys) propagate quickly.
  • Contributors: welcome @stgreenb 🎉 — author of the daemon-web merge and Docker packaging work.

Fixed

  • 🔒 Path traversal in dashboard static file serving (security): requests like GET /../../etc/passwd could escape web/dist/ and read arbitrary files reachable by the daemon user. The mitigation (127.0.0.1 bind) was bypassed inside Docker where the daemon binds 0.0.0.0. All static paths are now resolved against WEB_DIST_DIR and rejected with 403 when they escape.
  • 🔒 Cross-site write / key exfiltration on dashboard endpoints (security): POST /api/settings (writes API keys) and GET /api/key/<provider> (reveals raw keys) previously accepted any request, including those triggered by malicious tabs visiting a page that fetched http://localhost:19280/.... Both now enforce a same-origin / loopback Origin header check. Header-less CLI callers (curl, native apps) keep working.
  • 🔒 Config file permissions tightened in Docker: the entrypoint previously created ~/.free-coding-models.json with mode 0666. Tightened to 0600 since it stores plaintext API keys.
  • 🐛 /api/key/:provider route never matched: the handler compared url.pathname === '/api/key/:provider' literally, so the endpoint was unreachable. Now correctly matches via startsWith and 404s unknown providers.
  • 🐛 "Excellent" verdict for fully-down models: when a model had probe history but every ping failed, avg collapsed to 0 and the dashboard showed "Excellent". Verdict now correctly returns — whenever no usable latency sample exists.
  • 🐛 --daemon-bg mode signalled "down" for everything in dashboard: the type-mismatch comparison (=== between string code and number) made every model render as down. Codes are now compared as strings.
  • ⚙️ Docker GitHub Actions workflow YAML repaired: the top-level env: block and the GHCR login step were incorrectly indented and would have prevented GitHub from parsing the workflow at all.
  • ⚙️ Docker container lifecycle: the container no longer outlives a crashed daemon. The entrypoint now runs the daemon in the foreground (--daemon instead of --daemon-bg) so Docker's restart policy can recover from crashes instead of waiting for the healthcheck to time out.

Internal

  • Removed a dead createReadStream import in router-daemon.js.
  • Hoisted routerConfig() and getSet() lookups out of the per-model loop in getWebModelsPayload() — saves ~200 redundant runtime calls per dashboard refresh — and uses a Set index for in-set membership checks.
  • Renamed a local window variable to probeWindow to avoid shadowing globals.
  • Removed a nested router shadow in /api/settings.
  • Added X-Content-Type-Options: nosniff header on all static dashboard responses.
  • Added 6 new tests covering path-traversal blocking, cross-origin write rejection, same-origin write success, CLI key fetch, and unknown-provider 404. Total: 371 tests, all green.