docs: ultra-detailed README with icon, badges, screenshots; MIT license; publication gitignore
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Showing 6 changed files with +333 and −67
modified
.gitignore
+32 −4
@@ -1,9 +1,37 @@ | ||
| 1 | +# Build products | |
| 1 | 2 | .build/ |
| 3 | +dist/ | |
| 4 | +build/ | |
| 5 | +DerivedData/ | |
| 2 | 6 | *.app |
| 3 | 7 | *.icns |
| 4 | −AppIcon.iconset/ | |
| 5 | −.DS_Store | |
| 6 | −.env.test-keys | |
| 7 | −dist/ | |
| 8 | 8 | *.zip |
| 9 | 9 | *.dmg |
| 10 | +AppIcon.iconset/ | |
| 11 | + | |
| 12 | +# Xcode user state | |
| 13 | +*.xcuserstate | |
| 14 | +xcuserdata/ | |
| 15 | +*.xcworkspace/xcuserdata/ | |
| 16 | +*.pbxuser | |
| 17 | +*.mode1v3 | |
| 18 | +*.mode2v3 | |
| 19 | +*.perspectivev3 | |
| 20 | + | |
| 21 | +# Dependencies | |
| 22 | +Pods/ | |
| 23 | +Carthage/Build/ | |
| 24 | +.swiftpm/ | |
| 25 | + | |
| 26 | +# Secrets — never commit keys | |
| 27 | +.env.test-keys | |
| 28 | +.env* | |
| 29 | +*.pem | |
| 30 | +vault.zq | |
| 31 | + | |
| 32 | +# macOS | |
| 33 | +.DS_Store | |
| 34 | +.AppleDouble | |
| 35 | +.LSOverride | |
| 36 | +Icon? | |
| 37 | +._* | |
added
LICENSE
+21 −0
@@ -0,0 +1,21 @@ | ||
| 1 | +MIT License | |
| 2 | + | |
| 3 | +Copyright (c) 2026 Simon-Pierre Boucher | |
| 4 | + | |
| 5 | +Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 6 | +of this software and associated documentation files (the "Software"), to deal | |
| 7 | +in the Software without restriction, including without limitation the rights | |
| 8 | +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 9 | +copies of the Software, and to permit persons to whom the Software is | |
| 10 | +furnished to do so, subject to the following conditions: | |
| 11 | + | |
| 12 | +The above copyright notice and this permission notice shall be included in all | |
| 13 | +copies or substantial portions of the Software. | |
| 14 | + | |
| 15 | +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 16 | +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 17 | +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| 18 | +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 19 | +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 20 | +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |
| 21 | +SOFTWARE. | |
modified
README.md
+280 −63
@@ -1,121 +1,338 @@ | ||
| 1 | 1 | <div align="center"> |
| 2 | 2 | |
| 3 | +<img src="assets/readme/app-icon.png" width="180" alt="Zyquo Router icon" /> | |
| 4 | + | |
| 3 | 5 | # Zyquo Router |
| 4 | 6 | |
| 5 | −**Your Mac as a local LLM gateway — one OpenAI-compatible endpoint for every provider.** | |
| 7 | +### One local endpoint. Every AI provider. Spec-exact. | |
| 8 | + | |
| 9 | +*Turn your Mac into a private LLM gateway — 170 models from 12 providers behind a single OpenAI-compatible API.* | |
| 10 | + | |
| 11 | +<br/> | |
| 12 | + | |
| 13 | +[](https://github.com/spboucher-ai/zyquo-router/releases/latest) | |
| 14 | +[](#-requirements) | |
| 15 | +[](#%EF%B8%8F-building-from-source) | |
| 16 | +[](LICENSE) | |
| 17 | +[](https://github.com/spboucher-ai/zyquo-router/releases) | |
| 18 | + | |
| 19 | +[](#-installation) | |
| 20 | +[](#-installation) | |
| 21 | +[](#%EF%B8%8F-architecture) | |
| 22 | +[](#%EF%B8%8F-architecture) | |
| 6 | 23 | |
| 7 | −Native macOS · Swift + SwiftUI · built with SPM, no Xcode IDE | |
| 24 | +<br/> | |
| 8 | 25 | |
| 9 | −`Anthropic` · `OpenAI` · `xAI` · `Mistral` · `Gemini` · `Qwen` · `DeepSeek` · `Kimi` · `Perplexity` · `Together` · `DeepInfra` · `Cerebras` | |
| 26 | +`Anthropic` · `OpenAI` · `xAI` · `Mistral` · `Google Gemini` · `Alibaba Qwen` · `DeepSeek` · `Kimi` · `Perplexity` · `Together AI` · `DeepInfra` · `Cerebras` | |
| 10 | 27 | |
| 11 | 28 | </div> |
| 12 | 29 | |
| 13 | 30 | --- |
| 14 | 31 | |
| 15 | −Zyquo Router turns your Mac into a private, local LLM gateway. Store your provider API | |
| 16 | −keys once (encrypted AES-256-GCM vault — no Keychain), pick a port, hit **Start**, and | |
| 17 | −every tool that speaks the OpenAI API instantly gets **170 models from 12 providers** | |
| 18 | −through one normalized endpoint: | |
| 32 | +## 📖 Table of Contents | |
| 33 | + | |
| 34 | +- [Why Zyquo Router](#-why-zyquo-router) | |
| 35 | +- [Features](#-features) | |
| 36 | +- [Screenshots](#-screenshots) | |
| 37 | +- [Installation](#-installation) | |
| 38 | +- [Requirements](#-requirements) | |
| 39 | +- [Usage](#-usage) | |
| 40 | +- [Architecture](#%EF%B8%8F-architecture) | |
| 41 | +- [Building from Source](#%EF%B8%8F-building-from-source) | |
| 42 | +- [Verification](#-verification) | |
| 43 | +- [Roadmap](#-roadmap) | |
| 44 | +- [Contributing](#-contributing) | |
| 45 | +- [License](#-license) | |
| 46 | +- [Author](#-author) | |
| 19 | 47 | |
| 20 | −``` | |
| 21 | −http://localhost:8787/v1 | |
| 22 | −``` | |
| 48 | +--- | |
| 23 | 49 | |
| 24 | −Think *OpenRouter / LiteLLM — but native, local, private, and gorgeous.* | |
| 50 | +## 💡 Why Zyquo Router | |
| 25 | 51 | |
| 26 | −## Why | |
| 52 | +Every AI provider speaks a slightly different dialect. Anthropic wants `x-api-key` and | |
| 53 | +content blocks; Gemini wants `contents/parts` and camelCase; Mistral renames `seed`; | |
| 54 | +Perplexity ends its streams with a non-spec event. Your tools — SDKs, CLIs, IDE plugins, | |
| 55 | +agents — mostly speak **one** dialect: the OpenAI API. | |
| 27 | 56 | |
| 28 | −- **One request format, all providers.** Anthropic's Messages API and Gemini's | |
| 29 | − `generateContent` are translated bidirectionally into spec-exact OpenAI wire format — | |
| 30 | − streaming (byte-exact `chat.completion.chunk` SSE), tool calling, vision, JSON mode, | |
| 31 | − and reasoning included. The other nine providers get per-provider parameter | |
| 32 | − strip/rename/clamp tables and quirk normalization. | |
| 33 | −- **Private by design.** Keys live only in an encrypted vault on your Mac. The server | |
| 34 | − binds to localhost by default; LAN exposure requires a local API key. Logs are | |
| 35 | − redacted by default. No endpoint ever returns a key. | |
| 36 | −- **A control room, not a black box.** Live dashboard (requests/min, tokens, cost, | |
| 37 | − per-provider breakdown), full request inspector with a timing waterfall, model | |
| 38 | − aliases, fallback chains, a built-in playground, and in-app API docs. | |
| 57 | +**Zyquo Router** runs a tiny, native gateway on your Mac. You store your provider keys | |
| 58 | +once in an encrypted vault, pick a port, press **Start** — and everything that can talk | |
| 59 | +to OpenAI can now talk to *twelve providers* through `http://localhost:8787/v1`, with | |
| 60 | +per-request model routing, live traffic inspection, and cost tracking. | |
| 39 | 61 | |
| 40 | −## Quickstart | |
| 62 | +Think **OpenRouter / LiteLLM — but native, local, private, and gorgeous.** | |
| 41 | 63 | |
| 42 | −```bash | |
| 43 | −git clone <repo> && cd zyquo-router | |
| 44 | −make dev # build + assemble dist/Zyquo Router.app (ad-hoc signed) | |
| 45 | −open "dist/Zyquo Router.app" | |
| 46 | −``` | |
| 64 | +- 🔒 **Private by design** — keys never leave your Mac; requests go straight from your | |
| 65 | + machine to the provider. No middleman, no telemetry, no accounts. | |
| 66 | +- 🎯 **Spec-exact** — byte-exact `chat.completion.chunk` SSE streams that the official | |
| 67 | + OpenAI Python and JS SDKs parse **unmodified** (verified across all 170 models). | |
| 68 | +- 🖥 **A real Mac app** — SwiftUI control room with a menu bar extra, not a Docker | |
| 69 | + container with a YAML file. | |
| 70 | + | |
| 71 | +--- | |
| 72 | + | |
| 73 | +## ✨ Features | |
| 74 | + | |
| 75 | +### 🌐 The Gateway | |
| 76 | + | |
| 77 | +| Capability | Details | | |
| 78 | +|---|---| | |
| 79 | +| **OpenAI-compatible API** | `POST /v1/chat/completions` (streaming + non-streaming), `GET /v1/models`, `GET /v1/models/{id}`, `GET /health` | | |
| 80 | +| **170 models, 12 providers** | Full catalog with context windows, capabilities, and per-Mtok pricing under the `x_zyquo` extension key | | |
| 81 | +| **Namespaced routing** | `provider/model-id` (e.g. `anthropic/claude-sonnet-4-5`, `deepinfra/meta-llama/Llama-4-Maverick`); bare IDs accepted when unambiguous | | |
| 82 | +| **Aliases** | Friendly names — `fast` → `cerebras/…`, `best` → `anthropic/…` | | |
| 83 | +| **Fallback chains** | Ordered model lists tried on upstream failure; the response honestly reports the model that answered | | |
| 84 | +| **Full translation** | Anthropic Messages API and Gemini `generateContent` translated bidirectionally: system extraction, turn merging, tools ⇄ `tool_use`/`functionCall`, images, finish-reason and usage normalization | | |
| 85 | +| **Tool calling** | Streamed `tool_calls` argument deltas in exact OpenAI shape, from all three wire formats | | |
| 86 | +| **Vision** | `image_url` content parts (data-URI base64 and remote URLs where supported) | | |
| 87 | +| **Reasoning models** | Thinking output normalized to `reasoning_content` (DeepSeek convention) — works with Claude thinking, Gemini thoughts, DeepSeek-R1, Qwen, Magistral, Perplexity `<think>`, and more | | |
| 88 | +| **Quirk normalization** | Together `finish_reason:"eos"`, Mistral thinking arrays, Perplexity citations + `.done` events, missing `object` fields, argument-repeat streams — all ironed into the spec | | |
| 89 | +| **Resilience** | Exponential-backoff retries with jitter (never after the first streamed byte), honest OpenAI-format error mapping (401/429/502/504), client-disconnect cancels the upstream call in <1s | | |
| 90 | + | |
| 91 | +### 🔐 Control & Security | |
| 92 | + | |
| 93 | +- **Encrypted key vault** — AES-256-GCM with an HKDF-derived, machine-bound master key | |
| 94 | + (hardware UUID + salted pepper). No Keychain, no plaintext, ever. | |
| 95 | +- **Localhost by default** — LAN exposure (`0.0.0.0`) is an explicit opt-in that | |
| 96 | + *requires* at least one local API key. | |
| 97 | +- **Local API keys** — `zyquo-sk-…` bearer tokens, SHA-256-hashed at rest, shown once | |
| 98 | + at creation, per-key enable/revoke and model allow-lists. | |
| 99 | +- **Redacted logging** — request/response bodies hidden by default; revealing is an | |
| 100 | + explicit per-session switch. Provider keys never appear in any response, log, or error. | |
| 101 | + | |
| 102 | +### 📊 Observability | |
| 103 | + | |
| 104 | +- **Live dashboard** — requests/min sparkline, tokens, estimated cost (from real | |
| 105 | + per-model pricing), error rate, active streams, uptime, per-provider breakdown bar. | |
| 106 | +- **Request inspector** — every routed call with provider, status, latency, tokens, | |
| 107 | + cost, SSE badge, and a detail pane with an upstream-TTFB timing waterfall. | |
| 108 | +- **Filters & export** — by provider/status/model (⌘F), pause/clear, JSON export. | |
| 109 | + | |
| 110 | +### 🛠 Developer Experience | |
| 111 | + | |
| 112 | +- **Playground** — built-in tester that calls the router's *own* endpoint, with | |
| 113 | + side-by-side request JSON / raw SSE panes and copy-as-code. | |
| 114 | +- **In-app API docs** — the full reference rendered beautifully inside the app, from | |
| 115 | + the same source of truth the server implements. | |
| 116 | +- **Copy-as snippets** — endpoint URL, `curl`, OpenAI-Python, OpenAI-JS, pre-filled | |
| 117 | + with your port. | |
| 118 | +- **Menu bar extra** — status dot, Start/Stop, req/min, today's cost, copy endpoint. | |
| 119 | +- **⌘K command palette** — server control, section jumps, fuzzy model-ID copy. | |
| 120 | +- **Headless modes** — `ZyquoRouter --serve [port]` (no UI) and `--load-vault` | |
| 121 | + (seed keys from environment variables) for scripting and CI. | |
| 122 | +- **Shortcuts** — ⌘R start/stop · ⌘1–6 sections · ⌘F filter · ⌘⇧C copy endpoint · ⌘K palette. | |
| 123 | + | |
| 124 | +--- | |
| 125 | + | |
| 126 | +## 📸 Screenshots | |
| 127 | + | |
| 128 | +<div align="center"> | |
| 129 | + | |
| 130 | +**Dashboard — the control room** | |
| 131 | + | |
| 132 | +<img src="assets/readme/screenshot-dashboard.png" width="850" alt="Dashboard: server card with endpoint and copy-as snippets, live tiles, sparkline" /> | |
| 133 | + | |
| 134 | +<br/><br/> | |
| 135 | + | |
| 136 | +**In-app API reference** | |
| 137 | + | |
| 138 | +<img src="assets/readme/screenshot-docs.png" width="850" alt="Docs: hero card, code blocks with copy, styled sections" /> | |
| 139 | + | |
| 140 | +</div> | |
| 141 | + | |
| 142 | +--- | |
| 143 | + | |
| 144 | +## 📦 Installation | |
| 145 | + | |
| 146 | +### Download (recommended) | |
| 147 | + | |
| 148 | +1. **[⬇️ Download ZyquoRouter.dmg](https://github.com/spboucher-ai/zyquo-router/releases/latest/download/ZyquoRouter.dmg)** from the latest release | |
| 149 | +2. Open the DMG and drag **Zyquo Router** into **Applications** | |
| 150 | +3. Launch it — the app is **Developer ID signed, notarized, and stapled**, so Gatekeeper | |
| 151 | + opens it without warnings | |
| 152 | + | |
| 153 | +### First run — three steps to one endpoint | |
| 154 | + | |
| 155 | +1. **Keys** → paste the API keys for the providers you use (each row has a **Test** | |
| 156 | + button that verifies the key and shows latency) | |
| 157 | +2. **Dashboard** → pick a port (default `8787`) → press **Start** | |
| 158 | +3. Point anything OpenAI-compatible at `http://localhost:8787/v1` 🎉 | |
| 159 | + | |
| 160 | +--- | |
| 161 | + | |
| 162 | +## 🖥 Requirements | |
| 163 | + | |
| 164 | +| | Minimum | | |
| 165 | +|---|---| | |
| 166 | +| **macOS** | 13.0 Ventura or later | | |
| 167 | +| **Architecture** | Universal binary — Apple Silicon & Intel | | |
| 168 | +| **Disk** | ~15 MB | | |
| 169 | +| **Accounts** | None. Bring your own provider API keys | | |
| 47 | 170 | |
| 48 | −1. **Keys** → paste your provider API keys (each has a Test button). | |
| 49 | −2. **Dashboard** → pick a port → **Start**. | |
| 50 | −3. Point anything OpenAI-compatible at it: | |
| 171 | +--- | |
| 172 | + | |
| 173 | +## 🚀 Usage | |
| 174 | + | |
| 175 | +### Python (official OpenAI SDK — works unmodified) | |
| 51 | 176 | |
| 52 | 177 | ```python |
| 53 | 178 | from openai import OpenAI |
| 54 | 179 | |
| 55 | 180 | client = OpenAI(base_url="http://localhost:8787/v1", api_key="zyquo") |
| 56 | −r = client.chat.completions.create( | |
| 57 | − model="anthropic/claude-sonnet-4-5", # any provider/model from GET /v1/models | |
| 58 | − messages=[{"role": "user", "content": "Hello"}], | |
| 181 | + | |
| 182 | +stream = client.chat.completions.create( | |
| 183 | + model="anthropic/claude-sonnet-4-5", # any of the 170 models | |
| 184 | + messages=[{"role": "user", "content": "Hello!"}], | |
| 59 | 185 | stream=True, |
| 60 | 186 | ) |
| 61 | −for chunk in r: | |
| 187 | +for chunk in stream: | |
| 62 | 188 | print(chunk.choices[0].delta.content or "", end="") |
| 63 | 189 | ``` |
| 64 | 190 | |
| 191 | +### JavaScript / TypeScript | |
| 192 | + | |
| 65 | 193 | ```javascript |
| 66 | 194 | import OpenAI from "openai"; |
| 195 | + | |
| 67 | 196 | const client = new OpenAI({ baseURL: "http://localhost:8787/v1", apiKey: "zyquo" }); |
| 197 | +const r = await client.chat.completions.create({ | |
| 198 | + model: "gemini/gemini-2.5-flash", | |
| 199 | + messages: [{ role: "user", content: "Hello!" }], | |
| 200 | +}); | |
| 68 | 201 | ``` |
| 69 | 202 | |
| 203 | +### curl | |
| 204 | + | |
| 70 | 205 | ```bash |
| 71 | 206 | curl http://localhost:8787/v1/chat/completions \ |
| 72 | 207 | -H "Content-Type: application/json" \ |
| 73 | − -d '{"model": "deepseek/deepseek-chat", "messages": [{"role": "user", "content": "Hi"}]}' | |
| 208 | + -d '{"model": "deepseek/deepseek-chat", "messages": [{"role": "user", "content": "Hi"}], "stream": true}' | |
| 74 | 209 | ``` |
| 75 | 210 | |
| 76 | −Models are namespaced `provider/model-id`; bare IDs work when unambiguous, and you can | |
| 77 | −define aliases (`fast` → `cerebras/…`). `GET /v1/models` lists the full catalog with | |
| 78 | −context windows and pricing under the `x_zyquo` extension key. | |
| 211 | +### Good to know | |
| 79 | 212 | |
| 80 | −## Highlights | |
| 213 | +- **Model discovery:** `GET /v1/models` lists everything with context windows, pricing, | |
| 214 | + and capability flags. Disabled models 404; favorites float to the top in-app. | |
| 215 | +- **Reasoning:** pass the standard `reasoning_effort` — the router translates it per | |
| 216 | + provider (Anthropic thinking budgets, Gemini `thinkingConfig`, Mistral `prompt_mode`, …) | |
| 217 | + and normalizes the output into `reasoning_content`. | |
| 218 | +- **Provider extras** pass straight through: Perplexity `search_domain_filter`, Qwen | |
| 219 | + `enable_thinking`, Together `top_k`, Anthropic `thinking`, … | |
| 220 | +- **Auth:** with no local keys the router is open on localhost. Create `zyquo-sk-…` | |
| 221 | + keys in **Keys → Local API Keys** to require `Authorization: Bearer …` (mandatory for | |
| 222 | + LAN mode). | |
| 223 | +- The full contract lives in [`docs/API.md`](docs/API.md) — also rendered in-app under **Docs**. | |
| 81 | 224 | |
| 82 | −| | | | |
| 83 | −|---|---| | |
| 84 | −| **Spec-exact API** | `POST /v1/chat/completions` (streaming + non-streaming), `GET /v1/models`, `GET /health` — verified against the official OpenAI Python and JS SDKs, unmodified. | | |
| 85 | −| **Full translation** | Anthropic + Gemini native APIs ⇄ OpenAI format: tools, vision, reasoning (`reasoning_content`), usage normalization, finish-reason mapping. | | |
| 86 | −| **Resilience** | Exponential-backoff retries (never after the first streamed byte), user-defined fallback chains that honestly report the model that answered, client-disconnect cancels the upstream call. | | |
| 87 | −| **Access control** | Local `zyquo-sk-…` bearer keys (hashed at rest, shown once), per-key model allow-lists, LAN mode gated behind keys. | | |
| 88 | −| **Observability** | Live traffic table with filters, redacted-by-default body inspector, TTFB waterfall, cost estimates from real per-model pricing. | | |
| 89 | −| **Mac-native polish** | Menu bar extra, ⌘K command palette, launch-at-login, light-flagship theme with a graphite-cyan control-room identity. | | |
| 225 | +--- | |
| 226 | + | |
| 227 | +## 🏗️ Architecture | |
| 90 | 228 | |
| 91 | −## Building | |
| 229 | +100 % native Swift — no Electron, no Python sidecar, no Docker. | |
| 92 | 230 | |
| 93 | −Requirements: macOS 13+, Swift 5.9+ toolchain, `librsvg` (icon pipeline only). | |
| 231 | +``` | |
| 232 | +SwiftUI control room ─┐ | |
| 233 | + ├─► SwiftNIO HTTP/1.1 server (structured concurrency, | |
| 234 | +Menu bar extra ───────┘ NIOAsyncChannel, spec-exact SSE writer) | |
| 235 | + │ | |
| 236 | + RequestRouter (namespaces, aliases, | |
| 237 | + fallback chains, capability gates) | |
| 238 | + │ | |
| 239 | + ┌───────────────────┼────────────────────┐ | |
| 240 | + ▼ ▼ ▼ | |
| 241 | + AnthropicTranslator GeminiTranslator CompatAdjuster | |
| 242 | + (Messages API ⇄ (generateContent ⇄ (per-provider param | |
| 243 | + OpenAI, SSE event OpenAI, SSE tables + quirk | |
| 244 | + state machine) chunks) normalization ×10) | |
| 245 | + └───────────────────┼────────────────────┘ | |
| 246 | + ▼ | |
| 247 | + UpstreamCall → provider APIs (your keys, | |
| 248 | + straight from your Mac — AES-256-GCM vault) | |
| 249 | +``` | |
| 250 | + | |
| 251 | +- **Server:** SwiftNIO 2 with one task per connection; client disconnects propagate as | |
| 252 | + cancellation all the way into the upstream `URLSession` transfer. | |
| 253 | +- **Translation:** fixture-tested state machines convert Anthropic events and Gemini | |
| 254 | + chunks into byte-exact OpenAI `chat.completion.chunk`s. | |
| 255 | +- **Persistence:** JSON documents in `~/Library/Application Support/ZyquoRouter/`; | |
| 256 | + keys in `vault.zq` (AES-256-GCM, machine-bound HKDF key, no Keychain). | |
| 257 | +- **Zero heavyweight deps:** SwiftNIO, swift-nio-extras, swift-markdown. That's it. | |
| 258 | + | |
| 259 | +--- | |
| 260 | + | |
| 261 | +## ⚙️ Building from Source | |
| 262 | + | |
| 263 | +Requirements: macOS 13+, Swift 5.9+ toolchain (Xcode or CLT), `librsvg` for the icon | |
| 264 | +pipeline (`brew install librsvg`). Built entirely with SPM — **no Xcode IDE required**. | |
| 94 | 265 | |
| 95 | 266 | ```bash |
| 96 | −make dev # debug-friendly release build + ad-hoc signed .app | |
| 97 | −make test # swift test (unit, fixture, and gateway-behavior suites) | |
| 267 | +git clone https://github.com/spboucher-ai/zyquo-router.git | |
| 268 | +cd zyquo-router | |
| 269 | + | |
| 270 | +make dev # release build + ad-hoc signed dist/Zyquo Router.app | |
| 271 | +make test # unit, fixture, and gateway-behavior test suites | |
| 98 | 272 | make icon # regenerate AppIcon.icns from assets/icon/zyquo-router.svg |
| 99 | −make release # universal binary, Developer ID signing + notarization (Phase 8) | |
| 273 | +make release # universal binary + Developer ID signing + notarization + DMG | |
| 274 | +``` | |
| 275 | + | |
| 276 | +Headless gateway for scripts/CI: | |
| 277 | + | |
| 278 | +```bash | |
| 279 | +.build/release/ZyquoRouter --load-vault # seed the vault from env vars | |
| 280 | +.build/release/ZyquoRouter --serve 8787 # run the gateway without the UI | |
| 100 | 281 | ``` |
| 101 | 282 | |
| 102 | −Headless modes for scripting: `ZyquoRouter --serve [port]` runs the gateway without the | |
| 103 | −UI; `ZyquoRouter --load-vault` seeds the vault from environment variables. | |
| 283 | +--- | |
| 284 | + | |
| 285 | +## ✅ Verification | |
| 286 | + | |
| 287 | +Every release is verified by [`scripts/verify.py`](scripts/verify.py), which drives | |
| 288 | +**all 170 catalog models** through the local endpoint with the official OpenAI Python | |
| 289 | +SDK — non-streaming, streaming chunk discipline, tool calling, vision, and reasoning. | |
| 290 | +The current matrix is **170/170 green**: see [`docs/VERIFICATION.md`](docs/VERIFICATION.md). | |
| 291 | + | |
| 292 | +Gateway behavior is integration-tested against mock upstreams: client-disconnect | |
| 293 | +cancellation, fallback-chain honesty, 429 retries, and graceful shutdown with active | |
| 294 | +streams. | |
| 295 | + | |
| 296 | +--- | |
| 297 | + | |
| 298 | +## 🗺 Roadmap | |
| 299 | + | |
| 300 | +- [ ] `POST /v1/embeddings` routed to embedding-capable providers | |
| 301 | +- [ ] `POST /v1/responses` compatibility surface | |
| 302 | +- [ ] Prompt-level request/response caching | |
| 303 | +- [ ] Per-key rate limiting (token bucket) and budgets | |
| 304 | +- [ ] Usage history persistence with daily/weekly summaries | |
| 305 | +- [ ] Custom OpenAI-compatible endpoints (self-hosted vLLM/Ollama upstreams) | |
| 104 | 306 | |
| 105 | −## Documentation | |
| 307 | +--- | |
| 308 | + | |
| 309 | +## 🤝 Contributing | |
| 310 | + | |
| 311 | +Issues and pull requests are welcome! | |
| 312 | + | |
| 313 | +1. Fork → branch → make your change | |
| 314 | +2. `make test` must stay green (unit + fixture + gateway-behavior suites) | |
| 315 | +3. `swift build` must produce **zero warnings** | |
| 316 | +4. Every code file carries the project header (see any source file) | |
| 317 | + | |
| 318 | +--- | |
| 319 | + | |
| 320 | +## 📄 License | |
| 106 | 321 | |
| 107 | −- [`docs/API.md`](docs/API.md) — the exact public API contract (also rendered in-app) | |
| 108 | −- [`docs/VERIFICATION.md`](docs/VERIFICATION.md) — the 170-model compatibility matrix | |
| 109 | −- [`docs/ROUTER-RESEARCH.md`](docs/ROUTER-RESEARCH.md) — gateway research & design decisions | |
| 110 | −- [`docs/PROVIDER-REUSE.md`](docs/PROVIDER-REUSE.md) — Zyquo Cloud provider-layer study | |
| 111 | −- [`docs/PLAN.md`](docs/PLAN.md) — phase-by-phase execution log | |
| 322 | +Released under the [MIT License](LICENSE). | |
| 112 | 323 | |
| 113 | 324 | --- |
| 114 | 325 | |
| 326 | +## 👤 Author | |
| 327 | + | |
| 115 | 328 | <div align="center"> |
| 116 | 329 | |
| 330 | +**Simon-Pierre Boucher** | |
| 331 | + | |
| 332 | +📫 Contact: [contact@spboucher.ai](mailto:contact@spboucher.ai) | |
| 333 | + | |
| 117 | 334 | Part of the **Zyquo** family — Cloud · Local · Agent · Atlas · MLX · **Router** |
| 118 | 335 | |
| 119 | −© 2026 Simon-Pierre Boucher | |
| 336 | +<sub>© 2026 Simon-Pierre Boucher. All rights reserved.</sub> | |
| 120 | 337 | |
| 121 | 338 | </div> |
added
assets/readme/app-icon.png
+0 −0
Binary file not shown.
added
assets/readme/screenshot-dashboard.png
+0 −0
Binary file not shown.
added
assets/readme/screenshot-docs.png
+0 −0
Binary file not shown.