Spaces:
Runtime error
Runtime error
File size: 16,387 Bytes
cd8bd0a | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 | # Contributing to OmniRoute
Thank you for your interest in contributing! This guide covers everything you need to get started.
---
## Development Setup
### Prerequisites
- **Node.js** `>=22.22.3 <23`, or `>=24.0.0 <27` (recommended: 24 LTS)
- **npm** 10+
- **Git**
### Clone & Install
```bash
git clone https://github.com/diegosouzapw/OmniRoute.git
cd OmniRoute
npm install
```
### Environment Variables
```bash
# Create your .env from the template
cp .env.example .env
# Generate required secrets
echo "JWT_SECRET=$(openssl rand -base64 48)" >> .env
echo "API_KEY_SECRET=$(openssl rand -hex 32)" >> .env
```
Key variables for development:
| Variable | Development Default | Description |
| ---------------------- | ------------------------ | --------------------- |
| `PORT` | `20128` | Server port |
| `NEXT_PUBLIC_BASE_URL` | `http://localhost:20128` | Base URL for frontend |
| `JWT_SECRET` | (generate above) | JWT signing secret |
| `INITIAL_PASSWORD` | `CHANGEME` | First login password |
| `APP_LOG_LEVEL` | `info` | Log verbosity level |
### Dashboard Settings
The dashboard provides UI toggles for features that can also be configured via environment variables:
| Setting Location | Toggle | Description |
| ------------------- | ------------------ | ------------------------------ |
| Settings β Advanced | Debug Mode | Enable debug request logs (UI) |
| Settings β General | Sidebar Visibility | Show/hide sidebar sections |
These settings are stored in the database and persist across restarts, overriding env var defaults when set.
### Running Locally
```bash
# Development mode (hot reload)
npm run dev
# Production build
npm run build # next build β .build/next/ then assembleStandalone β dist/
npm run start
# Release build (clean rebuild + HEAD sentinel β required for deploy)
npm run build:release # rm -rf .build dist && build + writes dist/BUILD_SHA
# Common port configuration
PORT=20128 NEXT_PUBLIC_BASE_URL=http://localhost:20128 npm run dev
```
### Build Output Layout
| Directory | Contents | Tracked |
| --------- | ------------------------------------------------------------------------- | ------- |
| `src/` | Application source (TypeScript / TSX) | Yes |
| `.build/` | Intermediates β `next build` output (gitignored, `distDir = .build/next`) | No |
| `dist/` | Shippable bundle β assembled by `assembleStandalone` (gitignored) | No |
The build pipeline is a single pass:
```
npm run build
ββ next build β .build/next/standalone (Next.js output)
ββ assembleStandalone() (copies standalone + static + public + native assets)
ββ output: dist/ (server.js, .next/static/, public/, node_modules/)
```
`npm run build:release` additionally cleans both directories first and writes
`dist/BUILD_SHA` (= `git rev-parse --short HEAD`) as a deploy integrity sentinel.
> **VPS deploy note:** the remote image directory `/usr/lib/node_modules/omniroute/app/`
> is unchanged. The deploy skills rsync the contents of `dist/` into it.
> Only the in-repo build output path moved (`app/` β `dist/`).
Default URLs:
- **Dashboard**: `http://localhost:20128/dashboard`
- **API**: `http://localhost:20128/v1`
---
## Git Workflow
> β οΈ **NEVER commit directly to `main`.** Always use feature branches.
```bash
git checkout -b feat/your-feature-name
# ... make changes ...
git commit -m "feat: describe your change"
git push -u origin feat/your-feature-name
# Open a Pull Request on GitHub
```
### Branch Naming
| Prefix | Purpose |
| ----------- | ------------------------- |
| `feat/` | New features |
| `fix/` | Bug fixes |
| `refactor/` | Code restructuring |
| `docs/` | Documentation changes |
| `test/` | Test additions/fixes |
| `chore/` | Tooling, CI, dependencies |
### Commit Messages
Follow [Conventional Commits](https://www.conventionalcommits.org/):
```
feat: add circuit breaker for provider calls
fix: resolve JWT secret validation edge case
docs: update SECURITY.md with PII protection
test: add observability unit tests
refactor(db): consolidate rate limit tables
```
Scopes (v3.8): `db`, `sse`, `oauth`, `dashboard`, `api`, `cli`, `docker`, `ci`, `mcp`, `a2a`, `memory`, `skills`, `cloud-agent`, `guardrails`, `compression`, `auto-combo`, `resilience`, `providers`, `executors`, `translator`, `domain`, `authz`.
---
## Running Tests
```bash
# All tests (unit + vitest + ecosystem + e2e)
npm run test:all
# Single test file (Node.js native test runner β most tests use this)
node --import tsx/esm --test tests/unit/your-file.test.ts
# Vitest (MCP server, autoCombo, cache)
npm run test:vitest
# E2E tests (requires Playwright)
npm run test:e2e
# Protocol clients E2E (MCP transports, A2A)
npm run test:protocols:e2e
# Ecosystem compatibility tests
npm run test:ecosystem
# Coverage gate: 75% statements/lines/functions, 70% branches
npm run test:coverage
npm run coverage:report
# Lint + format check
npm run lint
npm run check
# Gated real-upstream combo smoke (requires VPS access + real provider credits)
# Hits REAL providers β costs a little. NEVER runs in CI. Skips cleanly without the gate.
# Needs: ssh root@192.168.0.15 access (sources a read-only DB snapshot from the VPS).
RUN_COMBO_LIVE=1 npm run test:combo:live
# Phase-3 VPS live smoke β plain Node ESM scripts, hit the live .15 server directly.
# Requires: ssh root@192.168.0.15 access (combos created/torn down via SSH sqlite).
# Hits REAL providers (small cost). Creates/deletes only __live_test__* combos. NEVER runs in CI.
# REQUIRE_API_KEY=false on .15 so no API key needed, but honors COMBO_LIVE_BASE_URL / COMBO_LIVE_API_KEY if set.
npm run test:combo:live:vps # 7 HTTP scenarios (priority/round-robin/weighted/cost/fusion/auto + health)
npm run test:combo:live:vps:failover # adds a real cross-provider failover scenario (8 total)
```
Coverage notes:
- `npm run test:coverage` measures source coverage for the main unit test suite, excludes `tests/**`, and includes `open-sse/**`
- Pull requests must keep the coverage gate at **75%+** statements/lines/functions and **70%+** branches
- If a PR changes production code in `src/`, `open-sse/`, `electron/`, or `bin/`, it must add or update automated tests in the same PR
- `npm run coverage:report` prints the detailed file-by-file report from the latest coverage run
- `npm run test:coverage:legacy` preserves the older metric for historical comparison
- See `docs/ops/COVERAGE_PLAN.md` for the phased coverage improvement roadmap
### Pull Request Requirements
Before opening or merging a PR:
- Run `npm run test:unit`
- Run `npm run test:coverage`
- Ensure the coverage gate stays at **75%+** statements/lines/functions, **70%+** branches
- Include the changed or added test files in the PR description when production code changed
- Check the SonarQube result on the PR when the project secrets are configured in CI
Current test status: **122 unit test files** covering:
- Provider translators and format conversion
- Rate limiting, circuit breaker, and resilience
- Semantic cache, idempotency, progress tracking
- Database operations and schema (21 DB modules)
- OAuth flows and authentication
- API endpoint validation (Zod v4)
- MCP server tools and scope enforcement
- Memory and Skills systems
---
## Code Style
- **ESLint** β Run `npm run lint` before committing
- **Prettier** β Auto-formatted via `lint-staged` on commit (2 spaces, semicolons, double quotes, 100 char width, es5 trailing commas)
- **TypeScript** β All `src/` code uses `.ts`/`.tsx`; `open-sse/` uses `.ts`/`.js`; document with TSDoc (`@param`, `@returns`, `@throws`)
- **No `eval()`** β ESLint enforces `no-eval`, `no-implied-eval`, `no-new-func`
- **Zod validation** β Use Zod v4 schemas for all API input validation
- **Naming**: Files = camelCase/kebab-case, components = PascalCase, constants = UPPER_SNAKE
---
## Project Structure
```
src/ # TypeScript (.ts / .tsx)
βββ app/ # Next.js 16 App Router
β βββ (dashboard)/ # Dashboard pages (23 sections)
β βββ api/ # API routes (51 directories)
β βββ login/ # Auth pages (.tsx)
βββ domain/ # Policy engine (policyEngine, comboResolver, costRules, etc.)
βββ lib/ # Core business logic (.ts)
β βββ a2a/ # Agent-to-Agent v0.3 protocol server
β βββ acp/ # Agent Communication Protocol registry
β βββ compliance/ # Compliance policy engine
β βββ db/ # SQLite database layer (21 modules + 16 migrations)
β βββ memory/ # Persistent conversational memory
β βββ oauth/ # OAuth providers, services, and utilities
β βββ skills/ # Extensible skill framework
β βββ usage/ # Usage tracking and cost calculation
β βββ localDb.ts # Re-export layer only β never add logic here
βββ middleware/ # Request middleware (promptInjectionGuard)
βββ mitm/ # MITM proxy (cert, DNS, target routing)
βββ shared/
β βββ components/ # React components (.tsx)
β βββ constants/ # Provider definitions (177), MCP scopes, 14 routing strategies
β βββ utils/ # Circuit breaker, sanitizer, auth helpers
β βββ validation/ # Zod v4 schemas
βββ sse/ # SSE proxy pipeline
open-sse/ # @omniroute/open-sse workspace
βββ executors/ # 14 provider-specific request executors
βββ handlers/ # 11 request handlers (chat, responses, embeddings, images, etc.)
βββ mcp-server/ # MCP server (25 tools, 3 transports, 10 scopes)
βββ services/ # 36+ services (combo, autoCombo, rateLimitManager, etc.)
βββ translator/ # Format translators (OpenAI β Claude β Gemini β Responses β Ollama)
βββ transformer/ # Responses API transformer
βββ utils/ # 22 utility modules (stream, TLS, proxy, logging)
electron/ # Electron desktop app (cross-platform)
tests/
βββ unit/ # Node.js test runner (1,574 test files)
βββ integration/ # Integration tests
βββ e2e/ # Playwright tests
βββ security/ # Security tests
βββ translator/ # Translator-specific tests
βββ load/ # Load tests
docs/
βββ adr/ # Architecture Decision Records
βββ architecture/ # System architecture & resilience
βββ comparison/ # OmniRoute vs alternatives
βββ compression/ # Compression guides & rules
βββ dev/ # Development guides
βββ diagrams/ # Architecture diagrams
βββ frameworks/ # MCP, A2A, OpenCode, Memory, Skills
βββ guides/ # User guide, Docker, setup, troubleshooting
βββ i18n/ # Internationalized README translations
βββ marketing/ # Marketing materials
βββ ops/ # Deployment, proxy, coverage, releases
βββ providers/ # Provider-specific docs
βββ reference/ # API reference, env vars, CLI tools, free tiers
βββ releases/ # Release notes
βββ routing/ # Auto-combo engine, reasoning replay
βββ screenshots/ # Dashboard screenshots
βββ security/ # Guardrails, compliance, stealth, tokens
βββ specs/ # Design specs
```
---
## Adding a New Provider
### Step 1: Register Provider Constants
Add to `src/shared/constants/providers.ts` β Zod-validated at module load.
### Step 2: Add Executor (if custom logic needed)
Create executor in `open-sse/executors/your-provider.ts` extending the base executor.
### Step 3: Add Translator (if non-OpenAI format)
Create request/response translators in `open-sse/translator/`.
### Step 4: Add OAuth Config (if OAuth-based)
Add OAuth credentials in `src/lib/oauth/constants/oauth.ts` and service in `src/lib/oauth/services/`.
If the upstream provider distributes a public OAuth client_id/secret or Firebase Web API key inside its public CLI / browser bundle, **do not** embed it as a string literal. Use `resolvePublicCred()` from `open-sse/utils/publicCreds.ts` and add a masked byte entry to `EMBEDDED_DEFAULTS`. The full mandatory workflow is documented in [`docs/security/PUBLIC_CREDS.md`](./docs/security/PUBLIC_CREDS.md).
Inside handlers/executors, error messages reaching the client must go through `buildErrorBody()` / `sanitizeErrorMessage()` from `open-sse/utils/error.ts` β never put raw `err.stack` or `err.message` in a Response body. See [`docs/security/ERROR_SANITIZATION.md`](./docs/security/ERROR_SANITIZATION.md).
### Step 5: Register Models
Add model definitions in `open-sse/config/providerRegistry.ts`.
### Step 6: Add Tests
Write unit tests in `tests/unit/` covering at minimum:
- Provider registration
- Request/response translation
- Error handling
---
## Pull Request Checklist
- [ ] Tests pass (`npm test`)
- [ ] Linting passes (`npm run lint`)
- [ ] Build succeeds (`npm run build`)
- [ ] TypeScript types added for new public functions and interfaces
- [ ] No hardcoded secrets or fallback values
- [ ] Public upstream credentials embedded via `resolvePublicCred()` (see [`docs/security/PUBLIC_CREDS.md`](./docs/security/PUBLIC_CREDS.md)), never as literals
- [ ] Error responses route through `buildErrorBody()` / `sanitizeErrorMessage()` β no raw stack traces in response bodies (see [`docs/security/ERROR_SANITIZATION.md`](./docs/security/ERROR_SANITIZATION.md))
- [ ] Shell commands (`exec` / `spawn`) pass runtime values via `env`, not via string interpolation
- [ ] All inputs validated with Zod schemas
- [ ] CHANGELOG updated (if user-facing change)
- [ ] Documentation updated (if applicable)
- [ ] No new CodeQL / Secret-Scanning alerts opened, or each one dismissed with technical justification referencing the relevant `docs/security/` doc
- [ ] Routes that spawn child processes (`/api/mcp/`, `/api/cli-tools/runtime/`) classified as `isLocalOnlyPath()` in `src/server/authz/routeGuard.ts` β see [Hard Rule #15](docs/security/ROUTE_GUARD_TIERS.md)
- [ ] No `Co-Authored-By` trailers in commit messages β commits must appear solely under the repository owner's Git identity (Hard Rule #16)
---
## Releasing
Releases are managed via the `/generate-release` workflow. When a new GitHub Release is created, the package is **automatically published to npm** via GitHub Actions.
For VPS deploys, use `npm run build:release` (not `npm run build`) β it performs a clean
rebuild, assembles the bundle into `dist/`, and writes the `dist/BUILD_SHA` sentinel.
Then use the `/deploy-vps-*-cc` skills which rsync `dist/` to the remote `app/` directory.
---
## Getting Help
- **Architecture**: See [`docs/architecture/ARCHITECTURE.md`](docs/architecture/ARCHITECTURE.md)
- **API Reference**: See [`docs/reference/API_REFERENCE.md`](docs/reference/API_REFERENCE.md)
- **Security docs**: [`docs/security/CLI_TOKEN.md`](docs/security/CLI_TOKEN.md), [`docs/security/ROUTE_GUARD_TIERS.md`](docs/security/ROUTE_GUARD_TIERS.md), [`docs/security/ERROR_SANITIZATION.md`](docs/security/ERROR_SANITIZATION.md), [`docs/security/PUBLIC_CREDS.md`](docs/security/PUBLIC_CREDS.md)
- **Ops docs**: [`docs/ops/SQLITE_RUNTIME.md`](docs/ops/SQLITE_RUNTIME.md)
- **Issues**: [github.com/diegosouzapw/OmniRoute/issues](https://github.com/diegosouzapw/OmniRoute/issues)
- **ADRs**: See `docs/adr/` for architectural decision records
|