Root cause of the packaged GUI crashing with SIGSEGV inside ld.so on user
machines (segfault at fixed +0x1ff36, undefined symbols nspr_use_zone_allocator
/ localtime64): the build host had LD_LIBRARY_PATH polluted by a third-party
runtime (/opt/cangjie), which electron-builder baked into the produced binary's
dependency resolution. Clean machines without that library then fail in the
dynamic loader before any app code runs.
- Makefile: every gui-* pack target now runs under `env -u LD_LIBRARY_PATH`
- main.js: app.disableHardwareAcceleration() before ready (avoids the common
Chromium GPU-process SIGSEGV on hybrid-GPU/Wayland Linux hosts)
- main.js: window icon reads from process.resourcesPath (real file, not asar)
— asar-path icons are a known GTK segfault source on Linux
- api/status sources now carry recent_ok/recent_err (last 300s real gateway
requests via Stats.SourceRecent) so a source actually serving traffic is
never shown as down just because probe /models got rate-limited
- WebUI source status column repaints every 5s (no more frozen-at-first-
render) with a manual refresh button; shows success rate + probe + cooldown
- tray menu status rows were enabled:false (GTK fixed light-grey, invisible
on light themes) — now enabled with no-op click and nativeTheme listener
rebuilds the menu on dark/light switches
- ignore local ops scripts (scripts/, machine-specific)
Window/taskbar icon never showed because the packaged .desktop had
StartupWMClass=ModelRouter while the live window WM_CLASS is the lowercase
app class 'modelrouter-gui' — no match, so GNOME fell back to the default
Electron icon.
- main.js: set BrowserWindow icon asset + backgroundColor (window paints
correctly with frameless titlebar and provides the taskbar's default icon)
- package.json: build.linux.desktop.entry.StartupWMClass now equals the real
WM_CLASS so packaged installs match too
- local integration: user-level .desktop installed at
~/.local/share/applications/modelrouter-gui.desktop (points at the unpacked
binary) so GNOME resolves the icon immediately
- coreReady now only flips after the embedded core actually answers HTTP
(fixes blank-screen race where the iframe loaded before the listener)
- renderer probes reachability, retries frame load with exponential backoff,
and recovers via onerror instead of giving up forever
- keep per-user theme in localStorage
- embedded profile no longer preconfigures a zen source (desktop users add
their own upstreams on the sources page)
- adapters/opencode.lua: opencode.ai zen free pool adapter — sends the
opencode client User-Agent (zen fingerprints clients by UA; non-official
clients hit FreeUsageLimitError); pairs with api_key: public
- config: no config file ships in the repo; first run generates a default
config at the -config path with a random admin key, loopback listen and a
keyless zen source (config.EnsureDefault); remove config.example.yaml
- lua: seed bundled adapters from the embedded FS instead of a hardcoded
name list
- ui: widen model kind select (chat was clipped to 'cha')
- phase 5 bugfixes: stats ms/s bucket mixing, cleanScopes nil, ctx.Err
guards, direct-path ModelAvailable, empty stream body failure,
bestImageModel rewrite, transform failure recording, Core.mu, timer,
effective model for tool-calls