Files
OmniRoute/electron
Diego Rodrigues de Sa e Souza bf8b56b29f Release v3.8.20 (#3547)
* chore(release): open v3.8.20 development cycle

* fix(images): prefer bare combos over image aliases (#3527)

Integrated into release/v3.8.20

* fix(translator): map Codex local_shell tool (#3534)

Integrated into release/v3.8.20

* fix(usage): make opencode-go quota fetcher fail-open instead of throwing 500 (#3522)

Integrated into release/v3.8.20

* Fix Runtime page breaker state rendering (#3533)

Integrated into release/v3.8.20

* Expose provider breaker degradation threshold setting (#3535)

Integrated into release/v3.8.20

* fix(executor): strip provider prefix from versioned built-in tool model field (#3532)

Integrated into release/v3.8.20

* feat(providers): add Claude Fable 5 support (#3524)

Integrated into release/v3.8.20

* feat(resilience): add global provider cooldown tracking to prevent combo re-walking (#3556)

Integrated into release/v3.8.20 (default OFF, opt-in)

* fix(translator): scope thoughtSignature bypass to Antigravity/CLI only (#3560)

Integrated into release/v3.8.20. Co-authored-by: Six7Day <six7day@gmail.com>

* fix(routing): normalize thinking:disabled for combo-substituted models that reject it (#3554) (#3563)

Integrated into release/v3.8.20

* fix(usage): accept 0/empty budget limits so the dashboard can save and clear (#3537) (#3564)

Integrated into release/v3.8.20

* docs(changelog): credit @Six7Day for #3560 thoughtSignature fix (#3414)

The #3560 squash co-author trailer landed inline (unparsed by GitHub), so add
an explicit CHANGELOG credit ensuring @Six7Day (original #3414) and @oyi77 are
on the public record for the Gemini thoughtSignature fix.

* fix(gamification): dedup badge unlock via user_badges so events don't re-fire every request (#3472) (#3565)

Integrated into release/v3.8.20

* fix(routing): pass through 'auto' keyword on codex /v1/responses instead of rewriting to codex/auto (#3509) (#3566)

Integrated into release/v3.8.20

* fix(cli-tools): normalize apiKey null in guide-settings schema so cloud-mode config saves (#3552) (#3567)

Integrated into release/v3.8.20

* fix(catalog): reclassify PublicAI from keyless to one-time-initial (requires API key) (#3558) (#3568)

Integrated into release/v3.8.20

* fix(gemini-web): surface missing Playwright browser as actionable 503 + cooldown hint, not a retryable 500 loop (#3516) (#3570)

Integrated into release/v3.8.20

* fix(security): sanitize raw err.message in web executors + embeddings/search response bodies (Rule #12) (#3494, #3495) (#3573)

Integrated into release/v3.8.20

* fix(dashboard): point CustomHostsManager + FeatureFlagsGrid at real routes (#3486, #3487) (#3574)

Integrated into release/v3.8.20

* chore(providers): remove dead krutrim entry (#3483) + docs(api): fix agent-bridge per-agent state route (#3489) (#3575)

Integrated into release/v3.8.20

* docs(api): correct API_REFERENCE.md paths for skills/plugins/admin/cache/acp/system-info (#3497) (#3577)

Integrated into release/v3.8.20

* fix(proxy): drive SOCKS5 UI option from runtime ENABLE_SOCKS5_PROXY, not build-time NEXT_PUBLIC (#3508) (#3579)

Integrated into release/v3.8.20

* fix(playground): filter playground models by node prefix so custom-endpoint models appear (#3505) (#3581)

Integrated into release/v3.8.20

* fix(usage): show an informative message instead of a blank Kiro quota card when no usage breakdown (#3506) (#3582)

Integrated into release/v3.8.20

* docs(changelog): add the #3506 Kiro quota entry (missed in #3582 due to a stale-base CHANGELOG anchor) (#3583)

Integrated into release/v3.8.20

* fix(auto-update): use stable PROJECT_ROOT walker, not frozen process.cwd() (#3561)

Integrated into release/v3.8.20. Auto-update PROJECT_ROOT now uses a stable __dirname-anchored upward walker instead of the no-op process.cwd() resolver.

* fix: address PR #3518 review comments (lifecycle hooks, regex, indentation, route params) (#3562)

Integrated into release/v3.8.20. Addresses #3518 review: regex literals, logs/[id] route params (Next 16), indentation, and wires plugin lifecycle hooks (onInstall/onActivate/onDeactivate/onUninstall) in the loader so manager.ts can register them. Adds Rule #18 regression test.

* docs(changelog): credit @ViFigueiredo (#3423) for PROJECT_ROOT + log #3561/#3562 (v3.8.20)

* fix: openai to gemini incorrectly translates historical tool calls into text (#3569)

Integrated into release/v3.8.20. Standard Gemini direct path now maps historical tool calls to native functionCall/functionResponse parts (signaturelessToolCallMode: native) instead of inert text — validated against the real Gemini API (gemini-2.5-flash returns 200 for signatureless native functionCall, even with tools+thinking; Hard Rule #18). Eliminates the text-serialization leak. Antigravity/CLI sentinel path (#3560) untouched.

* docs(changelog)+test: reconcile standard-Gemini native mode (#3569) — update round-2 rationale comment + log VPS validation

* docs(changelog): reconcile v3.8.20 — add 9 missing bullets + move [Unreleased] to versioned section

* docs(changelog): complete v3.8.20 reconciliation — 27 bullets, 11 contributors

---------

Co-authored-by: Alexander Averyanov <alex@averyan.ru>
Co-authored-by: Hakan Kurşun <bykamaka@gmail.com>
Co-authored-by: Wilson <pedbookmed@gmail.com>
Co-authored-by: Randi <55005611+rdself@users.noreply.github.com>
Co-authored-by: Giorgos Giakoumettis <giorgos@yiakoumettis.gr>
Co-authored-by: PizzaV <103120356+pizzav-xyz@users.noreply.github.com>
Co-authored-by: Paijo <14921983+oyi77@users.noreply.github.com>
Co-authored-by: Markus Hartung <mail@hartmark.se>
2026-06-10 13:49:08 -03:00
..
2026-06-06 19:13:11 -03:00
2026-06-07 07:20:02 -03:00
2026-06-10 13:49:08 -03:00
2026-06-10 13:49:08 -03:00
2026-06-06 19:13:11 -03:00
2026-06-07 07:20:02 -03:00
2026-05-26 23:51:47 -03:00

OmniRoute Electron Desktop App

This directory contains the Electron desktop application wrapper for OmniRoute.

Architecture (v1.6.4)

electron/
├── main.js          # Main process — window, tray, server lifecycle, CSP, IPC
├── preload.js       # Preload script — secure IPC bridge with disposer pattern
├── package.json     # Electron-specific dependencies & electron-builder config
├── types.d.ts       # TypeScript definitions (AppInfo, ServerStatus, ElectronAPI)
└── assets/          # Application icons and resources

src/shared/hooks/
└── useElectron.ts   # React hooks — useSyncExternalStore, zero re-renders

Key Design Decisions

Decision Rationale
waitForServer() polling Prevents blank screen on cold start — polls http://localhost:PORT before loading
stdio: 'pipe' Captures server stdout/stderr for logging + readiness detection (not inherit)
Disposer pattern onServerStatus() returns () => void for precise listener cleanup (no removeAllListeners)
useSyncExternalStore Zero re-renders for useIsElectron() — no useState + useEffect cycle
CSP via session headers Content-Security-Policy restricts script-src, connect-src etc. per Electron best practices
Platform-conditional titlebar titleBarStyle: 'hiddenInset' only on macOS; default on Windows/Linux

Development

Prerequisites

  1. Build the Next.js app first:
npm run build
  1. Install Electron dependencies:
cd electron
npm install

Running in Development

  1. Start the Next.js development server:
npm run dev
  1. In another terminal, start Electron:
cd electron
npm run dev

Running in Production Mode

  1. Build Next.js in standalone mode:
npm run build
  1. Start Electron:
cd electron
npm start

Building

Build for Current Platform

cd electron
npm run build

Build for Specific Platforms

# Windows
npm run build:win

# macOS (x64 + arm64)
npm run build:mac

# Linux
npm run build:linux

Output

Built applications are placed in dist-electron/:

  • Windows: .exe installer (NSIS) + portable .exe
  • macOS: .dmg installer (Intel + Apple Silicon)
  • Linux: .AppImage

Installation

macOS

  1. Download the latest .dmg from the Releases page.
  2. Open the .dmg file.
  3. Drag OmniRoute.app to the Applications folder.
  4. Launch from Applications.

⚠️ Note: The app is not signed with an Apple Developer certificate yet. If macOS blocks the app, run:

xattr -cr /Applications/OmniRoute.app

Or right-click the app → Open → Open (to bypass Gatekeeper on first launch).

Windows

Installer (Recommended):

  1. Download OmniRoute.Setup.*.exe from Releases.
  2. Run the installer.
  3. Launch from Start Menu or Desktop shortcut.

Portable (No Installation):

  1. Download OmniRoute.exe from Releases.
  2. Run directly from any folder.

Linux

  1. Download the .AppImage from Releases.
  2. Make it executable:
    chmod +x OmniRoute-*.AppImage
    
  3. Run:
    ./OmniRoute-*.AppImage
    

Features

  • Server Readiness — Waits for health check before showing window
  • System Tray — Minimize to tray with quick actions (open, port change, quit)
  • Port Management — Change port from tray menu (server restarts automatically)
  • Window Controls — Custom minimize, maximize, close via IPC
  • Content Security Policy — Restrictive CSP via session headers
  • Offline Support — Bundled Next.js standalone server
  • Single Instance — Only one app instance can run at a time

Configuration

Environment Variables

Variable Default Description
OMNIROUTE_PORT 20128 Server port
OMNIROUTE_MEMORY_MB 512 Node.js heap limit (6416384 MB)
NODE_ENV production Set to development for dev mode

Custom Icon

Place your icons in assets/:

  • icon.ico — Windows icon (256×256)
  • icon.icns — macOS icon bundle
  • icon.png — Linux/general use (512×512)
  • tray-icon.png — System tray icon (16×16 or 32×32)

IPC Channels

Invoke (Renderer → Main, async)

Channel Returns Description
get-app-info AppInfo App name, version, platform, isDev, port
open-external void Open URL in default browser (http/https only)
get-data-dir string Get userData directory path
restart-server { success } Stop + restart server (5s timeout + SIGKILL)

Send (Renderer → Main, fire-and-forget)

Channel Description
window-minimize Minimize window
window-maximize Toggle maximize/restore
window-close Close window (minimize to tray)

Receive (Main → Renderer, events)

Channel Payload Emitted When
server-status ServerStatus Server starts, stops, errors, or restarts
port-changed number Port change via tray menu

Note

: Listeners return disposer functions for precise cleanup. See useServerStatus and usePortChanged hooks.

Security

Feature Implementation
Context Isolation contextIsolation: true — renderer cannot access Node.js
Node Integration nodeIntegration: false — no require() in renderer
IPC Whitelist Channel names validated in preload via safeInvoke/safeSend/safeOn
URL Validation shell.openExternal() only allows http: / https: protocols
CSP Content-Security-Policy header set via session.webRequest.onHeadersReceived
Web Security webSecurity: true — same-origin policy enforced

React Hooks

Hook Returns Description
useIsElectron() boolean Zero-render detection via useSyncExternalStore
useElectronAppInfo() { appInfo, loading, error } App info from main process
useDataDir() { dataDir, loading, error } User data directory
useWindowControls() { minimize, maximize, close } Window control actions
useOpenExternal() { openExternal } Open URLs in browser
useServerControls() { restart, restarting } Server restart control
useServerStatus(cb) Disposer Listen for server status events
usePortChanged(cb) Disposer Listen for port change events

Troubleshooting

App Won't Start

  1. Check if port 20128 is available: lsof -i :20128
  2. Check console logs for [Electron] prefix
  3. Verify the build output exists in .build/next/standalone

White Screen

  1. Verify Next.js build exists — server readiness waits 30s max
  2. Check [Server] and [Server:err] log output
  3. Look for CSP violations in developer console

Build Fails

Ensure you have build tools installed:

  • Windows: Visual Studio Build Tools
  • macOS: Xcode Command Line Tools
  • Linux: build-essential, libsecret-1-dev

License

MIT