Compare commits
45 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f62dcc12a0 | |||
| 5907296d36 | |||
| aa2a7d12be | |||
| 33fee5dcc5 | |||
| e9ae50be0c | |||
| 5886c0fd5e | |||
| 9e640cac6b | |||
| 061521f87f | |||
| b15eb278e1 | |||
| 142ac8eb96 | |||
| 88705bb6e9 | |||
| 60d4fcfe7e | |||
| 038d19ec98 | |||
| e1b98768c7 | |||
| b82af2b849 | |||
| 703591d76a | |||
| 7142688a77 | |||
| a12622b3d8 | |||
| 9248ab4dfd | |||
| 5a8c6440f0 | |||
| 74b694a4dd | |||
| 896b52d5fb | |||
| 1429fea27a | |||
| 3218563f32 | |||
| d412edbbe1 | |||
| 968159a85d | |||
| 18a3741fc2 | |||
| f1be3e6bb0 | |||
| b717a02394 | |||
| d68143e63d | |||
| 0d306b8b1c | |||
| a655863855 | |||
| 58264c80dd | |||
| 6f9f1aec65 | |||
| 97b1ee5b02 | |||
| fe033cd0b3 | |||
| afbd07c62a | |||
| 9b15996545 | |||
| 1dbbd7241d | |||
| 6c0ef48d45 | |||
| 8b57f88ca3 | |||
| 3e9fdc777e | |||
| a8ca88797a | |||
| 71540b5dc0 | |||
| 23e3a1c269 |
@@ -29,7 +29,7 @@ jobs:
|
||||
uses: docker/setup-qemu-action@v4
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
uses: docker/setup-buildx-action@v4
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v4
|
||||
|
||||
@@ -201,3 +201,13 @@ jobs:
|
||||
release-assets/*.source.zip
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
publish-npm:
|
||||
name: Publish to npm
|
||||
needs: [validate, release]
|
||||
uses: ./.github/workflows/npm-publish.yml
|
||||
with:
|
||||
version: ${{ needs.validate.outputs.version }}
|
||||
tag: latest
|
||||
secrets:
|
||||
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
|
||||
@@ -6,9 +6,31 @@ on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
version:
|
||||
description: "Version tag to publish (e.g. 2.6.0)"
|
||||
description: "Version to publish (e.g. 2.9.5 or 3.0.0-rc.15)"
|
||||
required: true
|
||||
type: string
|
||||
tag:
|
||||
description: "npm dist-tag (latest / next)"
|
||||
required: false
|
||||
default: "latest"
|
||||
type: choice
|
||||
options:
|
||||
- latest
|
||||
- next
|
||||
workflow_call:
|
||||
inputs:
|
||||
version:
|
||||
description: "Version to publish (without v prefix)"
|
||||
required: true
|
||||
type: string
|
||||
tag:
|
||||
description: "npm dist-tag (latest / next)"
|
||||
required: false
|
||||
default: "latest"
|
||||
type: string
|
||||
secrets:
|
||||
NPM_TOKEN:
|
||||
required: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
@@ -31,16 +53,35 @@ jobs:
|
||||
- name: Install dependencies (skip scripts to avoid heavy build)
|
||||
run: npm install --ignore-scripts --no-audit --no-fund
|
||||
|
||||
- name: Sync version from release tag or input
|
||||
- name: Resolve version and dist-tag
|
||||
id: resolve
|
||||
run: |
|
||||
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
|
||||
VERSION="${{ inputs.version }}"
|
||||
else
|
||||
VERSION="${GITHUB_REF_NAME}"
|
||||
VERSION="${VERSION#v}"
|
||||
case "${{ github.event_name }}" in
|
||||
workflow_dispatch|workflow_call)
|
||||
VERSION="${{ inputs.version }}"
|
||||
TAG="${{ inputs.tag }}"
|
||||
;;
|
||||
release)
|
||||
VERSION="${GITHUB_REF_NAME}"
|
||||
;;
|
||||
esac
|
||||
# Strip v prefix if present
|
||||
VERSION="${VERSION#v}"
|
||||
# Default dist-tag logic
|
||||
if [ -z "$TAG" ]; then
|
||||
if [[ "$VERSION" == *-* ]]; then
|
||||
TAG="next"
|
||||
else
|
||||
TAG="latest"
|
||||
fi
|
||||
fi
|
||||
npm version "$VERSION" --no-git-tag-version --allow-same-version
|
||||
echo "Publishing version: $VERSION"
|
||||
echo "version=$VERSION" >> $GITHUB_OUTPUT
|
||||
echo "tag=$TAG" >> $GITHUB_OUTPUT
|
||||
echo "📦 Publishing omniroute@$VERSION with tag=$TAG"
|
||||
|
||||
- name: Sync package.json version
|
||||
run: |
|
||||
npm version "${{ steps.resolve.outputs.version }}" --no-git-tag-version --allow-same-version
|
||||
|
||||
- name: Build CLI bundle (standalone app)
|
||||
env:
|
||||
@@ -49,12 +90,18 @@ jobs:
|
||||
|
||||
- name: Publish to npm
|
||||
run: |
|
||||
VERSION=$(node -p "require('./package.json').version")
|
||||
VERSION="${{ steps.resolve.outputs.version }}"
|
||||
TAG="${{ steps.resolve.outputs.tag }}"
|
||||
# Check if this version is already published — skip instead of failing with E403
|
||||
if npm view "omniroute@${VERSION}" version --silent 2>/dev/null | grep -q "^${VERSION}$"; then
|
||||
echo "️⚠️ Version ${VERSION} is already published on npm — skipping."
|
||||
echo "⚠️ Version ${VERSION} is already published on npm — skipping."
|
||||
exit 0
|
||||
fi
|
||||
npm publish --access public
|
||||
if [ "$TAG" = "latest" ]; then
|
||||
npm publish --access public
|
||||
else
|
||||
npm publish --access public --tag "$TAG"
|
||||
fi
|
||||
echo "✅ Published omniroute@$VERSION (tag: $TAG)"
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
|
||||
@@ -49,19 +49,22 @@ but the real logic lives in `src/lib/db/`.
|
||||
|
||||
Translation between provider formats: `open-sse/translator/`
|
||||
|
||||
**Upstream model extra headers** (`compatByProtocol` / custom models): merged in executors after default auth; **same header name replaces** the executor value (e.g. custom `Authorization` overrides Bearer). In `open-sse/handlers/chatCore.ts`, the primary request merges headers for **both** the client model id and `resolveModelAlias(clientModel)` (resolved id wins on key conflicts). **T5 intra-family fallback** recomputes headers using only the fallback model id and `resolveModelAlias(fallback)` so sibling models do not inherit another model’s headers. Forbidden header names live in `src/shared/constants/upstreamHeaders.ts` — keep sanitize (`models.ts`), Zod (`schemas.ts`), and unit tests aligned when editing that list.
|
||||
|
||||
### MCP Server (`open-sse/mcp-server/`)
|
||||
|
||||
16 tools for AI agent control via **3 transport modes**:
|
||||
|
||||
- **stdio** — Local IDE integration (Claude Desktop, Cursor, VS Code)
|
||||
- **SSE** — Remote Server-Sent Events at `/api/mcp/sse`
|
||||
- **Streamable HTTP** — Modern bidirectional HTTP at `/api/mcp/stream`
|
||||
|
||||
HTTP transports run in-process via `httpTransport.ts` singleton using `WebStandardStreamableHTTPServerTransport`.
|
||||
|
||||
| Category | Tools |
|
||||
| ---------- | ------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Essential | `get_health`, `list_combos`, `get_combo_metrics`, `switch_combo`, `check_quota`, `route_request`, `cost_report`, `list_models_catalog` |
|
||||
| Advanced | `simulate_route`, `set_budget_guard`, `set_resilience_profile`, `test_combo`, `get_provider_metrics`, `best_combo_for_task`, `explain_route`, `get_session_snapshot` |
|
||||
| Category | Tools |
|
||||
| --------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Essential | `get_health`, `list_combos`, `get_combo_metrics`, `switch_combo`, `check_quota`, `route_request`, `cost_report`, `list_models_catalog` |
|
||||
| Advanced | `simulate_route`, `set_budget_guard`, `set_resilience_profile`, `test_combo`, `get_provider_metrics`, `best_combo_for_task`, `explain_route`, `get_session_snapshot` |
|
||||
|
||||
- Scoped authorization (9 scopes), audit logging, Zod schemas
|
||||
- IDE configs for Claude Desktop, Cursor, VS Code Copilot
|
||||
@@ -79,25 +82,26 @@ Agent-to-Agent v0.3 protocol:
|
||||
### Auto-Combo Engine (`open-sse/services/autoCombo/`)
|
||||
|
||||
Self-healing routing optimization:
|
||||
|
||||
- 6-factor scoring, 4 mode packs, bandit exploration
|
||||
- Progressive cooldown, probe-based re-admission
|
||||
|
||||
### Dashboard (`src/app/(dashboard)/`)
|
||||
|
||||
| Page | Description |
|
||||
| ---------------------------- | -------------------------------------------------------------- |
|
||||
| `/dashboard` | Home with quick start, provider overview |
|
||||
| `/dashboard/endpoint` | **Endpoints** (tabbed): Endpoint Proxy, MCP, A2A, API Endpoints |
|
||||
| `/dashboard/providers` | Provider management and connections |
|
||||
| `/dashboard/combos` | Combo configurations with routing strategies |
|
||||
| `/dashboard/logs` | Request, Proxy, Audit, Console logs (tabbed) |
|
||||
| `/dashboard/analytics` | Usage analytics and evaluations |
|
||||
| `/dashboard/costs` | Cost tracking and breakdown |
|
||||
| `/dashboard/health` | Uptime, circuit breakers, latency |
|
||||
| `/dashboard/cli-tools` | CLI tool integrations (Claude, Codex, Antigravity, etc.) |
|
||||
| `/dashboard/media` | Image, Video, Music generation playground |
|
||||
| `/dashboard/settings` | System settings with multiple tabs |
|
||||
| `/dashboard/api-manager` | API key management with model permissions |
|
||||
| Page | Description |
|
||||
| ------------------------ | --------------------------------------------------------------- |
|
||||
| `/dashboard` | Home with quick start, provider overview |
|
||||
| `/dashboard/endpoint` | **Endpoints** (tabbed): Endpoint Proxy, MCP, A2A, API Endpoints |
|
||||
| `/dashboard/providers` | Provider management and connections |
|
||||
| `/dashboard/combos` | Combo configurations with routing strategies |
|
||||
| `/dashboard/logs` | Request, Proxy, Audit, Console logs (tabbed) |
|
||||
| `/dashboard/analytics` | Usage analytics and evaluations |
|
||||
| `/dashboard/costs` | Cost tracking and breakdown |
|
||||
| `/dashboard/health` | Uptime, circuit breakers, latency |
|
||||
| `/dashboard/cli-tools` | CLI tool integrations (Claude, Codex, Antigravity, etc.) |
|
||||
| `/dashboard/media` | Image, Video, Music generation playground |
|
||||
| `/dashboard/settings` | System settings with multiple tabs |
|
||||
| `/dashboard/api-manager` | API key management with model permissions |
|
||||
|
||||
### OAuth & Tokens (`src/lib/oauth/`)
|
||||
|
||||
|
||||
@@ -1,294 +0,0 @@
|
||||
# Fixes Applied to PR #550 - Bot Review Responses
|
||||
|
||||
## Summary
|
||||
|
||||
Addressed all 4 WARNING issues identified by **kilo-code-bot** automated review.
|
||||
|
||||
---
|
||||
|
||||
## Issue #1: Potential undefined access - `cred.password` could be undefined
|
||||
|
||||
**File**: `src/lib/zed-oauth/keychain-reader.ts` (Line 99)
|
||||
**Problem**: `cred.password` accessed without null check
|
||||
|
||||
**Fix Applied**:
|
||||
|
||||
```typescript
|
||||
for (const cred of creds) {
|
||||
// FIX #1: Add null check for cred.password
|
||||
if (!cred.password) {
|
||||
console.debug(`Skipping credential with missing password: ${pattern}/${cred.account}`);
|
||||
continue;
|
||||
}
|
||||
|
||||
credentials.push({
|
||||
provider: extractProviderFromService(pattern),
|
||||
service: pattern,
|
||||
account: cred.account,
|
||||
token: cred.password,
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
**Result**: ✅ Credentials with missing passwords are now safely skipped with debug logging.
|
||||
|
||||
---
|
||||
|
||||
## Issue #2: Hardcoded account names may not match Zed's actual keychain naming
|
||||
|
||||
**File**: `src/lib/zed-oauth/keychain-reader.ts` (Line 125)
|
||||
**Problem**: Using hardcoded account name patterns without trying actual credentials first
|
||||
|
||||
**Fix Applied**:
|
||||
|
||||
```typescript
|
||||
/**
|
||||
* FIX #2: Instead of hardcoded account names, first try findCredentials
|
||||
* which will return all actual credentials for the service, then fallback
|
||||
* to common patterns only if needed.
|
||||
*/
|
||||
export async function getZedCredential(provider: string): Promise<ZedCredential | null> {
|
||||
const patterns = ZED_SERVICE_PATTERNS.filter((p) =>
|
||||
p.toLowerCase().includes(provider.toLowerCase())
|
||||
);
|
||||
|
||||
for (const pattern of patterns) {
|
||||
try {
|
||||
// First, try findCredentials to get all actual credentials
|
||||
const creds = await keytar.findCredentials(pattern);
|
||||
if (creds.length > 0 && creds[0].password) {
|
||||
return {
|
||||
provider,
|
||||
service: pattern,
|
||||
account: creds[0].account,
|
||||
token: creds[0].password,
|
||||
};
|
||||
}
|
||||
|
||||
// Fallback: Try common account name patterns
|
||||
const accountNames = ["api-key", "token", "oauth", provider];
|
||||
|
||||
for (const account of accountNames) {
|
||||
const token = await keytar.getPassword(pattern, account);
|
||||
if (token) {
|
||||
return {
|
||||
provider,
|
||||
service: pattern,
|
||||
account,
|
||||
token,
|
||||
};
|
||||
}
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.debug(`Failed to get credential for ${pattern}:`, error?.message || error);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
```
|
||||
|
||||
**Result**: ✅ Now tries actual credentials first, then falls back to common patterns only if needed.
|
||||
|
||||
---
|
||||
|
||||
## Issue #3: Inconsistent module style - uses CommonJS require() instead of ES import
|
||||
|
||||
**File**: `src/lib/zed-oauth/keychain-reader.ts` (Line 163)
|
||||
**Problem**: Using `require()` instead of ES imports
|
||||
|
||||
**Old Code**:
|
||||
|
||||
```typescript
|
||||
export async function isZedInstalled(): Promise<boolean> {
|
||||
const fs = require("fs");
|
||||
const os = require("os");
|
||||
const path = require("path");
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
**Fix Applied**:
|
||||
|
||||
```typescript
|
||||
// At top of file
|
||||
import fs from "fs";
|
||||
import os from "os";
|
||||
import path from "path";
|
||||
|
||||
/**
|
||||
* FIX #3: Convert to ES imports instead of CommonJS require()
|
||||
*/
|
||||
export async function isZedInstalled(): Promise<boolean> {
|
||||
const homeDir = os.homedir();
|
||||
const zedConfigPaths = [
|
||||
path.join(homeDir, ".config", "zed"), // Linux
|
||||
path.join(homeDir, "Library", "Application Support", "Zed"), // macOS
|
||||
path.join(homeDir, "AppData", "Roaming", "Zed"), // Windows
|
||||
];
|
||||
|
||||
for (const configPath of zedConfigPaths) {
|
||||
if (fs.existsSync(configPath)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
```
|
||||
|
||||
**Result**: ✅ Consistent ES module imports throughout the file.
|
||||
|
||||
---
|
||||
|
||||
## Issue #4: Incomplete implementation - credentials not actually imported into OmniRoute
|
||||
|
||||
**File**: `src/pages/api/providers/zed/import.ts` (originally)
|
||||
**Problem**: Credentials discovered but not integrated with OmniRoute's provider system
|
||||
|
||||
**Fix Applied**:
|
||||
|
||||
1. **Moved to correct directory structure** (App Router instead of Pages Router):
|
||||
- ❌ OLD: `src/pages/api/providers/zed/import.ts`
|
||||
- ✅ NEW: `src/app/api/providers/zed/import/route.ts`
|
||||
|
||||
2. **Updated to Next.js App Router format**:
|
||||
- Changed from `export default async function handler(req, res)`
|
||||
- To: `export async function POST(request: Request): Promise<NextResponse>`
|
||||
|
||||
3. **Added credential metadata response**:
|
||||
|
||||
```typescript
|
||||
// Return credential metadata (not actual tokens) for security
|
||||
const credentialSummary = credentials.map((cred) => ({
|
||||
provider: cred.provider,
|
||||
service: cred.service,
|
||||
account: cred.account,
|
||||
hasToken: Boolean(cred.token),
|
||||
}));
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
count: credentials.length,
|
||||
providers: uniqueProviders,
|
||||
credentials: credentialSummary, // NEW: Credential summary
|
||||
zedInstalled: true,
|
||||
});
|
||||
```
|
||||
|
||||
4. **Added maintainer integration notes**:
|
||||
|
||||
````typescript
|
||||
// FIX #4: Process and return credentials for integration
|
||||
//
|
||||
// MAINTAINER TODO: Integrate with OmniRoute's provider system here.
|
||||
//
|
||||
// Suggested integration points:
|
||||
// 1. Save to database using OmniRoute's provider schema
|
||||
// 2. Encrypt tokens using existing AES-256-GCM encryption
|
||||
// 3. Trigger provider registration hooks
|
||||
// 4. Update provider store state
|
||||
//
|
||||
// Example integration (pseudo-code):
|
||||
// ```
|
||||
// import { saveProvider, encryptCredential } from '@/lib/providers';
|
||||
//
|
||||
// for (const cred of credentials) {
|
||||
// await saveProvider({
|
||||
// type: cred.provider,
|
||||
// apiKey: await encryptCredential(cred.token),
|
||||
// source: 'zed-import',
|
||||
// enabled: true
|
||||
// });
|
||||
// }
|
||||
// ```
|
||||
````
|
||||
|
||||
**Result**: ✅ Credentials now properly discovered and returned in App Router format. Integration with OmniRoute's provider system documented for maintainer completion.
|
||||
|
||||
---
|
||||
|
||||
## Additional Improvements
|
||||
|
||||
### Better Error Handling
|
||||
|
||||
Added proper TypeScript error typing:
|
||||
|
||||
```typescript
|
||||
} catch (error: any) {
|
||||
console.error('[Zed Import] Error:', error);
|
||||
// Use optional chaining for error message
|
||||
if (error?.message?.includes('denied')) { ... }
|
||||
}
|
||||
```
|
||||
|
||||
### Linux Dependency Guidance
|
||||
|
||||
Improved error message for missing libsecret:
|
||||
|
||||
```typescript
|
||||
if (error?.message?.includes("not found")) {
|
||||
return NextResponse.json(
|
||||
{
|
||||
success: false,
|
||||
error: "Keychain service not available. On Linux, install libsecret-1-dev.",
|
||||
},
|
||||
{ status: 404 }
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Files Changed
|
||||
|
||||
1. **Modified**: `src/lib/zed-oauth/keychain-reader.ts`
|
||||
- Added null check for cred.password (Fix #1)
|
||||
- Prioritized actual credentials over hardcoded patterns (Fix #2)
|
||||
- Converted to ES imports (Fix #3)
|
||||
- Added proper TypeScript error types
|
||||
|
||||
2. **Deleted**: `src/pages/api/providers/zed/import.ts`
|
||||
- Wrong directory (Pages Router)
|
||||
|
||||
3. **Created**: `src/app/api/providers/zed/import/route.ts`
|
||||
- Correct App Router structure (Fix #4)
|
||||
- Credential metadata response
|
||||
- Maintainer integration notes
|
||||
|
||||
---
|
||||
|
||||
## Security Note (Addressing Bot Comment)
|
||||
|
||||
**Bot raised**: "References to security research about extracting secrets"
|
||||
|
||||
**Response**: The PR documentation references security research (Cycode blog) as **evidence** that the keychain extraction pattern is technically feasible and already proven in VS Code. This is **not** a vulnerability - it demonstrates:
|
||||
|
||||
1. **Industry Standard**: VS Code, GitHub Copilot CLI, and Claude Code all use this pattern
|
||||
2. **User-Initiated**: Extraction only happens when user explicitly clicks "Import from Zed"
|
||||
3. **OS-Protected**: Requires OS-level permission prompt that cannot be bypassed
|
||||
4. **Read-Only**: Only reads Zed-specific entries, no system-wide access
|
||||
|
||||
The reference is appropriate for technical justification, not an exploit guide.
|
||||
|
||||
---
|
||||
|
||||
## Testing Status
|
||||
|
||||
- ✅ TypeScript compiles without errors
|
||||
- ✅ Null checks added for undefined access
|
||||
- ✅ ES imports consistent throughout
|
||||
- ✅ App Router format correct
|
||||
- ⏳ Runtime testing pending (requires actual Zed installation)
|
||||
|
||||
---
|
||||
|
||||
## Next Steps
|
||||
|
||||
1. **For Maintainer**: Complete provider integration using suggested pattern in `route.ts`
|
||||
2. **For Reviewers**: Verify fixes address all bot warnings
|
||||
3. **For Testing**: Test with actual Zed IDE installation on macOS/Linux/Windows
|
||||
|
||||
---
|
||||
|
||||
**All 4 bot warnings addressed**. PR now follows OmniRoute's code conventions and App Router structure.
|
||||
@@ -2,11 +2,374 @@
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
> **Coming next** — see [3.0.0-rc branch](https://github.com/diegosouzapw/OmniRoute/tree/3.0.0-rc).
|
||||
---
|
||||
|
||||
## [3.0.1] — 2026-03-25
|
||||
|
||||
### 🔧 Hotfix Patch — Critical Bug Fixes
|
||||
|
||||
Three critical regressions reported by users after the v3.0.0 launch have been resolved.
|
||||
|
||||
#### fix(translator): strip `proxy_` prefix in non-streaming Claude responses (#605)
|
||||
|
||||
The `proxy_` prefix added by Claude OAuth was only stripped from **streaming** responses. In **non-streaming** mode, `translateNonStreamingResponse` had no access to the `toolNameMap`, causing clients to receive mangled tool names like `proxy_read_file` instead of `read_file`.
|
||||
|
||||
**Fix:** Added optional `toolNameMap` parameter to `translateNonStreamingResponse` and applied prefix stripping in the Claude `tool_use` block handler. `chatCore.ts` now passes the map through.
|
||||
|
||||
#### fix(validation): add LongCat specialty validator to skip /models probe (#592)
|
||||
|
||||
LongCat AI does not expose `GET /v1/models`. The generic `validateOpenAICompatibleProvider` validator fell through to a chat-completions fallback only if `validationModelId` was set, which LongCat doesn't configure. This caused provider validation to fail with a misleading error on add/save.
|
||||
|
||||
**Fix:** Added `longcat` to the specialty validators map, probing `/chat/completions` directly and treating any non-auth response as a pass.
|
||||
|
||||
#### fix(translator): normalize object tool schemas for Anthropic (#595)
|
||||
|
||||
MCP tools (e.g. `pencil`, `computer_use`) forward tool definitions with `{type:"object"}` but without a `properties` field. Anthropic's API rejects these with: `object schema missing properties`.
|
||||
|
||||
**Fix:** In `openai-to-claude.ts`, inject `properties: {}` as a safe default when `type` is `"object"` and `properties` is absent.
|
||||
|
||||
---
|
||||
|
||||
## [3.0.0-rc.15] — 2026-03-23
|
||||
### 🔀 Community PRs Merged (2)
|
||||
|
||||
| PR | Author | Summary |
|
||||
| -------- | ------- | -------------------------------------------------------------------------- |
|
||||
| **#589** | @flobo3 | docs(i18n): fix Russian translation for Playground and Testbed |
|
||||
| **#591** | @rdself | fix(ui): improve Provider Limits light mode contrast and plan tier display |
|
||||
|
||||
---
|
||||
|
||||
### ✅ Issues Resolved
|
||||
|
||||
`#592` `#595` `#605`
|
||||
|
||||
---
|
||||
|
||||
### 🧪 Tests
|
||||
|
||||
- **926 tests, 0 failures** (unchanged from v3.0.0)
|
||||
|
||||
---
|
||||
|
||||
## [3.0.0] — 2026-03-24
|
||||
|
||||
### 🎉 OmniRoute v3.0.0 — The Free AI Gateway, Now with 67+ Providers
|
||||
|
||||
> **The biggest release ever.** From 36 providers in v2.9.5 to **67+ providers** in v3.0.0 — with MCP Server, A2A Protocol, auto-combo engine, Provider Icons, Registered Keys API, 926 tests, and contributions from **12 community members** across **10 merged PRs**.
|
||||
>
|
||||
> Consolidated from v3.0.0-rc.1 through rc.17 (17 release candidates over 3 days of intense development).
|
||||
|
||||
---
|
||||
|
||||
### 🆕 New Providers (+31 since v2.9.5)
|
||||
|
||||
| Provider | Alias | Tier | Notes |
|
||||
| ----------------------------- | --------------- | ----------- | --------------------------------------------------------------------------- |
|
||||
| **OpenCode Zen** | `opencode-zen` | Free | 3 models via `opencode.ai/zen/v1` (PR #530 by @kang-heewon) |
|
||||
| **OpenCode Go** | `opencode-go` | Paid | 4 models via `opencode.ai/zen/go/v1` (PR #530 by @kang-heewon) |
|
||||
| **LongCat AI** | `lc` | Free | 50M tokens/day (Flash-Lite) + 500K/day (Chat/Thinking) during public beta |
|
||||
| **Pollinations AI** | `pol` | Free | No API key needed — GPT-5, Claude, Gemini, DeepSeek V3, Llama 4 (1 req/15s) |
|
||||
| **Cloudflare Workers AI** | `cf` | Free | 10K Neurons/day — ~150 LLM responses or 500s Whisper audio, edge inference |
|
||||
| **Scaleway AI** | `scw` | Free | 1M free tokens for new accounts — EU/GDPR compliant (Paris) |
|
||||
| **AI/ML API** | `aiml` | Free | $0.025/day free credits — 200+ models via single endpoint |
|
||||
| **Puter AI** | `pu` | Free | 500+ models (GPT-5, Claude Opus 4, Gemini 3 Pro, Grok 4, DeepSeek V3) |
|
||||
| **Alibaba Cloud (DashScope)** | `ali` | Paid | International + China endpoints via `alicode`/`alicode-intl` |
|
||||
| **Alibaba Coding Plan** | `bcp` | Paid | Alibaba Model Studio with Anthropic-compatible API |
|
||||
| **Kimi Coding (API Key)** | `kmca` | Paid | Dedicated API-key-based Kimi access (separate from OAuth) |
|
||||
| **MiniMax Coding** | `minimax` | Paid | International endpoint |
|
||||
| **MiniMax (China)** | `minimax-cn` | Paid | China-specific endpoint |
|
||||
| **Z.AI (GLM-5)** | `zai` | Paid | Zhipu AI next-gen GLM models |
|
||||
| **Vertex AI** | `vertex` | Paid | Google Cloud — Service Account JSON or OAuth access_token |
|
||||
| **Ollama Cloud** | `ollamacloud` | Paid | Ollama's hosted API service |
|
||||
| **Synthetic** | `synthetic` | Paid | Passthrough models gateway |
|
||||
| **Kilo Gateway** | `kg` | Paid | Passthrough models gateway |
|
||||
| **Perplexity Search** | `pplx-search` | Paid | Dedicated search-grounded endpoint |
|
||||
| **Serper Search** | `serper-search` | Paid | Web search API integration |
|
||||
| **Brave Search** | `brave-search` | Paid | Brave Search API integration |
|
||||
| **Exa Search** | `exa-search` | Paid | Neural search API integration |
|
||||
| **Tavily Search** | `tavily-search` | Paid | AI search API integration |
|
||||
| **NanoBanana** | `nb` | Paid | Image generation API |
|
||||
| **ElevenLabs** | `el` | Paid | Text-to-speech voice synthesis |
|
||||
| **Cartesia** | `cartesia` | Paid | Ultra-fast TTS voice synthesis |
|
||||
| **PlayHT** | `playht` | Paid | Voice cloning and TTS |
|
||||
| **Inworld** | `inworld` | Paid | AI character voice chat |
|
||||
| **SD WebUI** | `sdwebui` | Self-hosted | Stable Diffusion local image generation |
|
||||
| **ComfyUI** | `comfyui` | Self-hosted | ComfyUI local workflow node-based generation |
|
||||
| **GLM Coding** | `glm` | Paid | BigModel/Zhipu coding-specific endpoint |
|
||||
|
||||
**Total: 67+ providers** (4 Free, 8 OAuth, 55 API Key) + unlimited OpenAI/Anthropic-Compatible custom providers.
|
||||
|
||||
---
|
||||
|
||||
### ✨ Major Features
|
||||
|
||||
#### 🔑 Registered Keys Provisioning API (#464)
|
||||
|
||||
Auto-generate and issue OmniRoute API keys programmatically with per-provider and per-account quota enforcement.
|
||||
|
||||
| Endpoint | Method | Description |
|
||||
| ------------------------------- | ------------ | ------------------------------------------------ |
|
||||
| `/api/v1/registered-keys` | `POST` | Issue a new key — raw key returned **once only** |
|
||||
| `/api/v1/registered-keys` | `GET` | List registered keys (masked) |
|
||||
| `/api/v1/registered-keys/{id}` | `GET/DELETE` | Get metadata / Revoke |
|
||||
| `/api/v1/quotas/check` | `GET` | Pre-validate quota before issuing |
|
||||
| `/api/v1/providers/{id}/limits` | `GET/PUT` | Configure per-provider issuance limits |
|
||||
| `/api/v1/accounts/{id}/limits` | `GET/PUT` | Configure per-account issuance limits |
|
||||
| `/api/v1/issues/report` | `POST` | Report quota events to GitHub Issues |
|
||||
|
||||
**Security:** Keys stored as SHA-256 hashes. Raw key shown once on creation, never retrievable again.
|
||||
|
||||
#### 🎨 Provider Icons via @lobehub/icons (#529)
|
||||
|
||||
130+ provider logos using `@lobehub/icons` React components (SVG). Fallback chain: **Lobehub SVG → existing PNG → generic icon**. Applied across Dashboard, Providers, and Agents pages with standardized `ProviderIcon` component.
|
||||
|
||||
#### 🔄 Model Auto-Sync Scheduler (#488)
|
||||
|
||||
Auto-refreshes model lists for connected providers every **24 hours**. Runs on server startup. Configurable via `MODEL_SYNC_INTERVAL_HOURS`.
|
||||
|
||||
#### 🔀 Per-Model Combo Routing (#563)
|
||||
|
||||
Map model name patterns (glob) to specific combos for automatic routing:
|
||||
|
||||
- `claude-sonnet*` → code-combo, `gpt-4o*` → openai-combo, `gemini-*` → google-combo
|
||||
- New `model_combo_mappings` table with glob-to-regex matching
|
||||
- Dashboard UI section: "Model Routing Rules" with inline add/edit/toggle/delete
|
||||
|
||||
#### 🧭 API Endpoints Dashboard
|
||||
|
||||
Interactive catalog, webhooks management, OpenAPI viewer — all in one tabbed page at `/dashboard/endpoint`.
|
||||
|
||||
#### 🔍 Web Search Providers
|
||||
|
||||
5 new search provider integrations: **Perplexity Search**, **Serper**, **Brave Search**, **Exa**, **Tavily** — enabling grounded AI responses with real-time web data.
|
||||
|
||||
#### 📊 Search Analytics
|
||||
|
||||
New tab in `/dashboard/analytics` — provider breakdown, cache hit rate, cost tracking. API: `GET /api/v1/search/analytics`.
|
||||
|
||||
#### 🛡️ Per-API-Key Rate Limits (#452)
|
||||
|
||||
`max_requests_per_day` and `max_requests_per_minute` columns with in-memory sliding-window enforcement returning HTTP 429.
|
||||
|
||||
#### 🎵 Media Playground
|
||||
|
||||
Full media generation playground at `/dashboard/media`: Image Generation, Video, Music, Audio Transcription (2GB upload limit), and Text-to-Speech.
|
||||
|
||||
---
|
||||
|
||||
### 🔒 Security & CI/CD
|
||||
|
||||
- **CodeQL remediation** — Fixed 10+ alerts: 6 polynomial-redos, 1 insecure-randomness (`Math.random()` → `crypto.randomUUID()`), 1 shell-command-injection
|
||||
- **Route validation** — Zod schemas + `validateBody()` on **176/176 API routes** — CI enforced
|
||||
- **CVE fix** — dompurify XSS vulnerability (GHSA-v2wj-7wpq-c8vv) resolved via npm overrides
|
||||
- **Flatted** — Bumped 3.3.3 → 3.4.2 (CWE-1321 prototype pollution)
|
||||
- **Docker** — Upgraded `docker/setup-buildx-action` v3 → v4
|
||||
|
||||
---
|
||||
|
||||
### 🐛 Bug Fixes (40+)
|
||||
|
||||
#### OAuth & Auth
|
||||
|
||||
- **#537** — Gemini CLI OAuth: clear actionable error when `GEMINI_OAUTH_CLIENT_SECRET` missing in Docker
|
||||
- **#549** — CLI settings routes now resolve real API key from `keyId` (not masked strings)
|
||||
- **#574** — Login no longer freezes after skipping wizard password setup
|
||||
- **#506** — Cross-platform `machineId` rewritten (Windows REG.exe → macOS ioreg → Linux → hostname fallback)
|
||||
|
||||
#### Providers & Routing
|
||||
|
||||
- **#536** — LongCat AI: fixed `baseUrl` and `authHeader`
|
||||
- **#535** — Pinned model override: `body.model` correctly set to `pinnedModel`
|
||||
- **#570** — Unprefixed Claude models now resolve to Anthropic provider
|
||||
- **#585** — `<omniModel>` internal tags no longer leak to clients in SSE streaming
|
||||
- **#493** — Custom provider model naming no longer mangled by prefix stripping
|
||||
- **#490** — Streaming + context cache protection via `TransformStream` injection
|
||||
- **#511** — `<omniModel>` tag injected into first content chunk (not after `[DONE]`)
|
||||
|
||||
#### CLI & Tools
|
||||
|
||||
- **#527** — Claude Code + Codex loop: `tool_result` blocks now converted to text
|
||||
- **#524** — OpenCode config saved correctly (XDG_CONFIG_HOME, TOML format)
|
||||
- **#522** — API Manager: removed misleading "Copy masked key" button
|
||||
- **#546** — `--version` returning `unknown` on Windows (PR by @k0valik)
|
||||
- **#544** — Secure CLI tool detection via known installation paths (PR by @k0valik)
|
||||
- **#510** — Windows MSYS2/Git-Bash paths normalized automatically
|
||||
- **#492** — CLI detects `mise`/`nvm`-managed Node when `app/server.js` missing
|
||||
|
||||
#### Streaming & SSE
|
||||
|
||||
- **PR #587** — Revert `resolveDataDir` import in responsesTransformer for Cloudflare Workers compat (@k0valik)
|
||||
- **PR #495** — Bottleneck 429 infinite wait: drop waiting jobs on rate limit (@xandr0s)
|
||||
- **#483** — Stop trailing `data: null` after `[DONE]` signal
|
||||
- **#473** — Zombie SSE streams: timeout reduced 300s → 120s for faster fallback
|
||||
|
||||
#### Media & Transcription
|
||||
|
||||
- **Transcription** — Deepgram `video/mp4` → `audio/mp4` MIME mapping, auto language detection, punctuation
|
||||
- **TTS** — `[object Object]` error display fixed for ElevenLabs-style nested errors
|
||||
- **Upload limits** — Media transcription increased to 2GB (nginx `client_max_body_size 2g` + `maxDuration=300`)
|
||||
|
||||
---
|
||||
|
||||
### 🔧 Infrastructure & Improvements
|
||||
|
||||
#### Sub2api Gap Analysis (T01–T15 + T23–T42)
|
||||
|
||||
- **T01** — `requested_model` column in call logs (migration 009)
|
||||
- **T02** — Strip empty text blocks from nested `tool_result.content`
|
||||
- **T03** — Parse `x-codex-5h-*` / `x-codex-7d-*` quota headers
|
||||
- **T04** — `X-Session-Id` header for external sticky routing
|
||||
- **T05** — Rate-limit DB persistence with dedicated API
|
||||
- **T06** — Account deactivated → permanent block (1-year cooldown)
|
||||
- **T07** — X-Forwarded-For IP validation (`extractClientIp()`)
|
||||
- **T08** — Per-API-key session limits with sliding-window enforcement
|
||||
- **T09** — Codex vs Spark rate-limit scopes (separate pools)
|
||||
- **T10** — Credits exhausted → distinct 1h cooldown fallback
|
||||
- **T11** — `max` reasoning effort → 131072 budget tokens
|
||||
- **T12** — MiniMax M2.7 pricing entries
|
||||
- **T13** — Stale quota display fix (reset window awareness)
|
||||
- **T14** — Proxy fast-fail TCP check (≤2s, cached 30s)
|
||||
- **T15** — Array content normalization for Anthropic
|
||||
- **T23** — Intelligent quota reset fallback (header extraction)
|
||||
- **T24** — `503` cooldown + `406` mapping
|
||||
- **T25** — Provider validation fallback
|
||||
- **T29** — Vertex AI Service Account JWT auth
|
||||
- **T33** — Thinking level to budget conversion
|
||||
- **T36** — `403` vs `429` error classification
|
||||
- **T38** — Centralized model specifications (`modelSpecs.ts`)
|
||||
- **T39** — Endpoint fallback for `fetchAvailableModels`
|
||||
- **T41** — Background task auto-redirect to flash models
|
||||
- **T42** — Image generation aspect ratio mapping
|
||||
|
||||
#### Other Improvements
|
||||
|
||||
- **Per-model upstream custom headers** — via configuration UI (PR #575 by @zhangqiang8vip)
|
||||
- **Model context length** — configurable in model metadata (PR #578 by @hijak)
|
||||
- **Model prefix stripping** — option to remove provider prefix from model names (PR #582 by @jay77721)
|
||||
- **Gemini CLI deprecation** — marked deprecated with Google OAuth restriction warning
|
||||
- **YAML parser** — replaced custom parser with `js-yaml` for correct OpenAPI spec parsing
|
||||
- **ZWS v5** — HMR leak fix (485 DB connections → 1, memory 2.4GB → 195MB)
|
||||
- **Log export** — New JSON export button on dashboard with time range dropdown
|
||||
- **Update notification banner** — dashboard homepage shows when new versions are available
|
||||
|
||||
---
|
||||
|
||||
### 🌐 i18n & Documentation
|
||||
|
||||
- **30 languages** at 100% parity — 2,788 missing keys synced
|
||||
- **Czech** — Full translation: 22 docs, 2,606 UI strings (PR by @zen0bit)
|
||||
- **Chinese (zh-CN)** — Complete retranslation (PR by @only4copilot)
|
||||
- **VM Deployment Guide** — Translated to English as source document
|
||||
- **API Reference** — Added `/v1/embeddings` and `/v1/audio/speech` endpoints
|
||||
- **Provider count** — Updated from 36+/40+/44+ to **67+** across README and all 30 i18n READMEs
|
||||
|
||||
---
|
||||
|
||||
### 🔀 Community PRs Merged (10)
|
||||
|
||||
| PR | Author | Summary |
|
||||
| -------- | --------------- | -------------------------------------------------------------------- |
|
||||
| **#587** | @k0valik | fix(sse): revert resolveDataDir import for Cloudflare Workers compat |
|
||||
| **#582** | @jay77721 | feat(proxy): model name prefix stripping option |
|
||||
| **#581** | @jay77721 | fix(npm): link electron-release to npm-publish workflow |
|
||||
| **#578** | @hijak | feat: configurable context length in model metadata |
|
||||
| **#575** | @zhangqiang8vip | feat: per-model upstream headers, compat PATCH, chat alignment |
|
||||
| **#562** | @coobabm | fix: MCP session management, Claude passthrough, detectFormat |
|
||||
| **#561** | @zen0bit | fix(i18n): Czech translation corrections |
|
||||
| **#555** | @k0valik | fix(sse): centralized `resolveDataDir()` for path resolution |
|
||||
| **#546** | @k0valik | fix(cli): `--version` returning `unknown` on Windows |
|
||||
| **#544** | @k0valik | fix(cli): secure CLI tool detection via installation paths |
|
||||
| **#542** | @rdself | fix(ui): light mode contrast CSS theme variables |
|
||||
| **#530** | @kang-heewon | feat: OpenCode Zen + Go providers with `OpencodeExecutor` |
|
||||
| **#512** | @zhangqiang8vip | feat: per-protocol model compatibility (`compatByProtocol`) |
|
||||
| **#497** | @zhangqiang8vip | fix: dev-mode HMR resource leaks (ZWS v5) |
|
||||
| **#495** | @xandr0s | fix: Bottleneck 429 infinite wait (drop waiting jobs) |
|
||||
| **#494** | @zhangqiang8vip | feat: MiniMax developer→system role fix |
|
||||
| **#480** | @prakersh | fix: stream flush usage extraction |
|
||||
| **#479** | @prakersh | feat: Codex 5.3/5.4 and Anthropic pricing entries |
|
||||
| **#475** | @only4copilot | feat(i18n): improved Chinese translation |
|
||||
|
||||
**Thank you to all contributors!** 🙏
|
||||
|
||||
---
|
||||
|
||||
### 📋 Issues Resolved (50+)
|
||||
|
||||
`#452` `#458` `#462` `#464` `#466` `#473` `#474` `#481` `#483` `#487` `#488` `#489` `#490` `#491` `#492` `#493` `#506` `#508` `#509` `#510` `#511` `#513` `#520` `#521` `#522` `#524` `#525` `#527` `#529` `#531` `#532` `#535` `#536` `#537` `#541` `#546` `#549` `#563` `#570` `#574` `#585`
|
||||
|
||||
---
|
||||
|
||||
### 🧪 Tests
|
||||
|
||||
- **926 tests, 0 failures** (up from 821 in v2.9.5)
|
||||
- +105 new tests covering: model-combo mappings, registered keys, OpencodeExecutor, Bailian provider, route validation, error classification, aspect ratio mapping, and more
|
||||
|
||||
---
|
||||
|
||||
### 📦 Database Migrations
|
||||
|
||||
| Migration | Description |
|
||||
| --------- | --------------------------------------------------------------------- |
|
||||
| **008** | `registered_keys`, `provider_key_limits`, `account_key_limits` tables |
|
||||
| **009** | `requested_model` column in `call_logs` |
|
||||
| **010** | `model_combo_mappings` table for per-model combo routing |
|
||||
|
||||
---
|
||||
|
||||
### ⬆️ Upgrading from v2.9.5
|
||||
|
||||
```bash
|
||||
# npm
|
||||
npm install -g omniroute@3.0.0
|
||||
|
||||
# Docker
|
||||
docker pull diegosouzapw/omniroute:3.0.0
|
||||
|
||||
# Migrations run automatically on first startup
|
||||
```
|
||||
|
||||
> **Breaking changes:** None. All existing configurations, combos, and API keys are preserved.
|
||||
> Database migrations 008-010 run automatically on startup.
|
||||
|
||||
---
|
||||
|
||||
## [3.0.0-rc.17] — 2026-03-24
|
||||
|
||||
### 🔒 Security & CI/CD
|
||||
|
||||
- **CodeQL remediation** — Fixed 10+ alerts:
|
||||
- 6 polynomial-redos in `provider.ts` / `chatCore.ts` (replaced `(?:^|/)` alternation patterns with segment-based matching)
|
||||
- 1 insecure-randomness in `acp/manager.ts` (`Math.random()` → `crypto.randomUUID()`)
|
||||
- 1 shell-command-injection in `prepublish.mjs` (`JSON.stringify()` path escaping)
|
||||
- **Route validation** — Added Zod schemas + `validateBody()` to 5 routes missing validation:
|
||||
- `model-combo-mappings` (POST, PUT), `webhooks` (POST, PUT), `openapi/try` (POST)
|
||||
- CI `check:route-validation:t06` now passes: **176/176 routes validated**
|
||||
|
||||
### 🐛 Bug Fixes
|
||||
|
||||
- **#585** — `<omniModel>` internal tags no longer leak to clients in SSE responses. Added outbound sanitization `TransformStream` in `combo.ts`
|
||||
|
||||
### ⚙️ Infrastructure
|
||||
|
||||
- **Docker** — Upgraded `docker/setup-buildx-action` from v3 → v4 (Node.js 20 deprecation fix)
|
||||
- **CI cleanup** — Deleted 150+ failed/cancelled workflow runs
|
||||
|
||||
### 🧪 Tests
|
||||
|
||||
- Test suite: **926 tests, 0 failures** (+3 new)
|
||||
|
||||
---
|
||||
|
||||
## [3.0.0-rc.16] — 2026-03-24
|
||||
|
||||
### ✨ New Features
|
||||
|
||||
- Increased media transcription limits
|
||||
- Added Model Context Length to registry metadata
|
||||
- Added per-model upstream custom headers via configuration UI
|
||||
- Fixed multiple bugs, Zod valiadation for patches, and resolved various community issues.
|
||||
|
||||
## [3.0.0-rc.15] — 2026-03-24
|
||||
|
||||
### ✨ New Features
|
||||
|
||||
@@ -19,6 +382,23 @@
|
||||
- Dashboard: "Model Routing Rules" section added to Combos page with inline add/edit/toggle/delete
|
||||
- Examples: `claude-sonnet*` → code-combo, `gpt-4o*` → openai-combo, `gemini-*` → google-combo
|
||||
|
||||
### 🌐 i18n
|
||||
|
||||
- **Full i18n Sync**: 2,788 missing keys added across 30 language files — all languages now at 100% parity with `en.json`
|
||||
- **Agents page i18n**: OpenCode Integration section fully internationalized (title, description, scanning, download labels)
|
||||
- **6 new keys** added to `agents` namespace for OpenCode section
|
||||
|
||||
### 🎨 UI/UX
|
||||
|
||||
- **Provider Icons**: 16 missing provider icons added (3 copied, 2 downloaded, 11 SVG created)
|
||||
- **SVG fallback**: `ProviderIcon` component updated with 4-tier strategy: Lobehub → PNG → SVG → Generic icon
|
||||
- **Agents fingerprinting**: Synced with CLI tools — added droid, openclaw, copilot, opencode to fingerprint list (14 total)
|
||||
|
||||
### 🔒 Security
|
||||
|
||||
- **CVE fix**: Resolved dompurify XSS vulnerability (GHSA-v2wj-7wpq-c8vv) via npm overrides forcing `dompurify@^3.3.2`
|
||||
- `npm audit` now reports **0 vulnerabilities**
|
||||
|
||||
### 🧪 Tests
|
||||
|
||||
- Test suite: **923 tests, 0 failures** (+15 new model-combo mapping tests)
|
||||
|
||||
@@ -1,207 +0,0 @@
|
||||
# Add Zed IDE OAuth Import Support
|
||||
|
||||
## Summary
|
||||
|
||||
This PR adds support for importing OAuth credentials from **Zed IDE** into OmniRoute. Zed IDE stores OAuth tokens in the OS keychain (as documented in [official Zed docs](https://zed.dev/docs/ai/llm-providers)), and this feature allows users to automatically discover and import those credentials with one click.
|
||||
|
||||
## Problem Statement
|
||||
|
||||
Zed IDE users who want to use OmniRoute currently have to:
|
||||
|
||||
1. Manually copy API keys from Zed settings
|
||||
2. Paste them into OmniRoute dashboard
|
||||
3. Manage tokens separately in two places
|
||||
|
||||
This creates friction and duplicates credential management.
|
||||
|
||||
## Solution
|
||||
|
||||
Implemented a **keychain-based credential extractor** that:
|
||||
|
||||
- ✅ Automatically discovers OAuth tokens from OS keychain
|
||||
- ✅ Supports macOS (Keychain), Windows (Credential Manager), Linux (libsecret)
|
||||
- ✅ Works with all major Zed providers: OpenAI, Anthropic, Google, Mistral, xAI, OpenRouter, DeepSeek
|
||||
- ✅ One-click import from dashboard
|
||||
- ✅ Secure: Uses OS-level keychain permissions
|
||||
|
||||
## Technical Details
|
||||
|
||||
### Implementation Pattern
|
||||
|
||||
This follows the **proven pattern** used by:
|
||||
|
||||
- **VS Code** - Uses `keytar` for Secret Storage API
|
||||
- **GitHub Copilot CLI** - Stores OAuth tokens in OS keychain
|
||||
- **Claude Code CLI** - Stores OAuth in macOS Keychain
|
||||
|
||||
### Files Added
|
||||
|
||||
1. **`src/lib/zed-oauth/keychain-reader.ts`**
|
||||
- Core credential extraction logic
|
||||
- Cross-platform keychain access via `keytar` library
|
||||
- Auto-discovers all Zed OAuth tokens
|
||||
|
||||
2. **`src/pages/api/providers/zed/import.ts`**
|
||||
- API endpoint: `POST /api/providers/zed/import`
|
||||
- Handles credential discovery and import
|
||||
- Returns provider list and count
|
||||
|
||||
3. **`docs/zed-oauth-import.md`**
|
||||
- Complete documentation
|
||||
- Usage instructions
|
||||
- Security considerations
|
||||
|
||||
### Dependencies
|
||||
|
||||
Requires **`keytar`** library (already used by Electron apps):
|
||||
|
||||
```bash
|
||||
npm install keytar
|
||||
```
|
||||
|
||||
**Linux users** need `libsecret` development files:
|
||||
|
||||
```bash
|
||||
# Debian/Ubuntu
|
||||
sudo apt-get install libsecret-1-dev
|
||||
|
||||
# Red Hat/Fedora
|
||||
sudo yum install libsecret-devel
|
||||
|
||||
# Arch Linux
|
||||
sudo pacman -S libsecret
|
||||
```
|
||||
|
||||
## Zed Documentation Evidence
|
||||
|
||||
From [Zed's official documentation](https://zed.dev/docs/ai/llm-providers):
|
||||
|
||||
> **"Note: API keys are not stored as plain text in your settings file, but rather in your OS's secure credential storage."**
|
||||
|
||||
This is stated **8+ times** in the official docs for different providers (OpenAI, Anthropic, Mistral, xAI, etc.).
|
||||
|
||||
## Similar Implementations
|
||||
|
||||
This pattern is proven and used by:
|
||||
|
||||
1. **VS Code Extensions**
|
||||
- Source: https://cycode.com/blog/exposing-vscode-secrets/
|
||||
- Uses `keytar` for credential storage
|
||||
- Security research confirms extraction feasibility
|
||||
|
||||
2. **GitHub Copilot CLI**
|
||||
- Source: https://docs.github.com/en/copilot/how-tos/copilot-cli/set-up-copilot-cli/authenticate-copilot-cli
|
||||
- Stores tokens in OS keychain by default
|
||||
- Falls back to plaintext config if unavailable
|
||||
|
||||
3. **Claude Code CLI**
|
||||
- Source: https://code.claude.com/docs/en/authentication
|
||||
- macOS Keychain storage
|
||||
- Community requested token export feature
|
||||
|
||||
## Security Considerations
|
||||
|
||||
### User Consent
|
||||
|
||||
- First keychain access triggers **OS-level permission prompt**
|
||||
- User must explicitly grant access
|
||||
- No way to bypass system security
|
||||
|
||||
### Data Handling
|
||||
|
||||
- Tokens extracted only when user clicks "Import from Zed"
|
||||
- Encrypted in OmniRoute database (existing AES-256-GCM encryption)
|
||||
- Never stored in plaintext logs
|
||||
- Minimal keychain access scope (read-only, Zed-specific entries)
|
||||
|
||||
### Audit Trail
|
||||
|
||||
- All import attempts logged
|
||||
- Failed access attempts tracked
|
||||
- Compatible with existing OmniRoute audit system
|
||||
|
||||
## Usage
|
||||
|
||||
### For End Users
|
||||
|
||||
1. Navigate to `/dashboard/providers`
|
||||
2. Click **"Import from Zed IDE"** button
|
||||
3. Grant OS keychain permission when prompted
|
||||
4. Credentials automatically discovered and imported
|
||||
|
||||
### For Developers
|
||||
|
||||
```typescript
|
||||
import { discoverZedCredentials } from "@/lib/zed-oauth/keychain-reader";
|
||||
|
||||
// Discover all Zed credentials
|
||||
const credentials = await discoverZedCredentials();
|
||||
|
||||
// Get specific provider
|
||||
const openaiCred = await getZedCredential("openai");
|
||||
```
|
||||
|
||||
## Testing
|
||||
|
||||
Tested on:
|
||||
|
||||
- ✅ macOS (Keychain Access)
|
||||
- ✅ Linux (Ubuntu with libsecret)
|
||||
- ⚠️ Windows (requires testing - see below)
|
||||
|
||||
### Testing Checklist
|
||||
|
||||
- [ ] Verify keychain permission prompt appears on first access
|
||||
- [ ] Test import with multiple Zed providers configured
|
||||
- [ ] Test behavior when Zed is not installed
|
||||
- [ ] Test keychain access denial handling
|
||||
- [ ] Verify credentials encrypted in OmniRoute database
|
||||
- [ ] Test on Windows with Credential Manager
|
||||
|
||||
## Future Enhancements
|
||||
|
||||
1. **Dashboard UI Component** (not included in this PR)
|
||||
- Visual "Import from Zed IDE" button
|
||||
- Progress indicator during discovery
|
||||
- List of discovered providers
|
||||
|
||||
2. **Auto-refresh Integration**
|
||||
- Hook into OmniRoute's existing token refresh system
|
||||
- Keep Zed and OmniRoute tokens in sync
|
||||
|
||||
3. **Zed Extension** (long-term)
|
||||
- Official Zed marketplace extension
|
||||
- Secure token sharing without keychain extraction
|
||||
- Two-way credential sync
|
||||
|
||||
## Breaking Changes
|
||||
|
||||
None. This is a purely additive feature.
|
||||
|
||||
## Related Issues
|
||||
|
||||
Closes: (reference issue if exists)
|
||||
Relates to: Community request in OmniRoute Telegram group (screenshot attached)
|
||||
|
||||
## References
|
||||
|
||||
- [Zed LLM Providers Documentation](https://zed.dev/docs/ai/llm-providers)
|
||||
- [keytar Library (GitHub)](https://github.com/atom/node-keytar)
|
||||
- [VS Code Secret Storage Vulnerability Research](https://cycode.com/blog/exposing-vscode-secrets/)
|
||||
- [GitHub Copilot CLI Authentication](https://docs.github.com/en/copilot/how-tos/copilot-cli/set-up-copilot-cli/authenticate-copilot-cli)
|
||||
- [Claude Code Authentication](https://code.claude.com/docs/en/authentication)
|
||||
|
||||
## Screenshots
|
||||
|
||||
_(Dashboard UI component will be added in follow-up PR)_
|
||||
|
||||
---
|
||||
|
||||
## Maintainer Notes
|
||||
|
||||
- Implementation follows OmniRoute's TypeScript conventions
|
||||
- No changes to existing provider system
|
||||
- Backward compatible with current OAuth flows
|
||||
- Documentation included in `/docs` directory
|
||||
|
||||
**Ready for review!** 🚀
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
### Never stop coding. Smart routing to **FREE & low-cost AI models** with automatic fallback.
|
||||
|
||||
_Your universal API proxy — one endpoint, 44+ providers, zero downtime. Now with **MCP & A2A** agent orchestration._
|
||||
_Your universal API proxy — one endpoint, 67+ providers, zero downtime. Now with **MCP & A2A** agent orchestration._
|
||||
|
||||
**Chat Completions • Embeddings • Image Generation • Video • Music • Audio • Reranking • **Web Search** • MCP Server • A2A Protocol • 100% TypeScript**
|
||||
|
||||
@@ -32,6 +32,9 @@ _Your universal API proxy — one endpoint, 44+ providers, zero downtime. Now wi
|
||||
|
||||
| Area | Change |
|
||||
| ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| 🔒 **CodeQL Security** | Fixed 10+ CodeQL alerts: polynomial-redos, insecure-randomness, shell-injection remediation |
|
||||
| ✅ **Route Validation** | All 176 API routes now validated with Zod schemas + `validateBody()` — CI `check:route-validation:t06` passes |
|
||||
| 🐛 **omniModel Tag Leak** | Internal `<omniModel>` tags no longer leak to clients in SSE streaming responses (#585) |
|
||||
| 🔑 **Registered Keys API** | Auto-provision API keys via `POST /api/v1/registered-keys` with per-provider/account quota enforcement, idempotency, SHA-256 storage, and optional GitHub issue reporting |
|
||||
| 🎨 **Provider Icons** | 130+ provider logos via `@lobehub/icons` (SVG) with PNG → generic fallback chain |
|
||||
| 🔄 **Model Auto-Sync** | 24h scheduler refreshes model lists for 16 providers on startup — configurable via `MODEL_SYNC_INTERVAL_HOURS` |
|
||||
@@ -255,7 +258,7 @@ OpenAI uses one format, Claude (Anthropic) uses another, Gemini yet another. If
|
||||
|
||||
**How OmniRoute solves it:**
|
||||
|
||||
- **Unified Endpoint** — A single `http://localhost:20128/v1` serves as proxy for all 44+ providers
|
||||
- **Unified Endpoint** — A single `http://localhost:20128/v1` serves as proxy for all 67+ providers
|
||||
- **Format Translation** — Automatic and transparent: OpenAI ↔ Claude ↔ Gemini ↔ Responses API
|
||||
- **Response Sanitization** — Strips non-standard fields (`x_groq`, `usage_breakdown`, `service_tier`) that break OpenAI SDK v1.83+
|
||||
- **Role Normalization** — Converts `developer` → `system` for non-OpenAI providers; `system` → `user` for GLM/ERNIE
|
||||
@@ -341,7 +344,7 @@ Developers use Cursor, Claude Code, Codex CLI, OpenClaw, Gemini CLI, Kilo Code..
|
||||
- **CLI Tools Dashboard** — Dedicated page with one-click setup for Claude Code, Codex CLI, OpenClaw, Kilo Code, Antigravity, Cline
|
||||
- **GitHub Copilot Config Generator** — Generates `chatLanguageModels.json` for VS Code with bulk model selection
|
||||
- **Onboarding Wizard** — Guided 4-step setup for first-time users
|
||||
- **One endpoint, all models** — Configure `http://localhost:20128/v1` once, access 44+ providers
|
||||
- **One endpoint, all models** — Configure `http://localhost:20128/v1` once, access 67+ providers
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
@@ -1,374 +0,0 @@
|
||||
# ZWS_README_V4 — 启动性能优化:HMR 泄漏修复与 Turbopack 迁移
|
||||
|
||||
## 一、如何发现问题
|
||||
|
||||
### 现象
|
||||
|
||||
- `npm run dev` 后,首次打开浏览器白屏等待 **5-22 秒**不等。
|
||||
- 运行一段时间后 Node 进程内存飙升至 **2.4 GB**,触发 Next.js 内存阈值保护强制重启。
|
||||
- 重启后 `Ready in 82.6s`(正常冷启动仅 3.4s),之后每个页面首次编译需 **7-28 秒**。
|
||||
- 日志中大量重复输出,单次会话内:
|
||||
- `[DB] SQLite database ready` 出现 **485 次**
|
||||
- `[HealthCheck] Starting proactive token health-check` 出现 **586 次**
|
||||
- `[CREDENTIALS] No external credentials file found` 出现 **432 次**
|
||||
|
||||
### 排查过程
|
||||
|
||||
1. **Terminal 日志分析**:统计关键日志出现次数,发现 DB 连接和 HealthCheck 定时器被反复创建。
|
||||
2. **代码审计**:追踪到所有受影响模块使用 `let initialized = false` 作为单例守卫——这在 Next.js dev 模式的 Webpack HMR 下会被重置。
|
||||
3. **对比**:`apiBridgeServer.ts` 使用了 `globalThis.__omnirouteApiBridgeStarted`,在日志中无重复初始化,验证了 `globalThis` 方案的有效性。
|
||||
4. **内存快照**:通过 `Get-Process node` 观察到两个 node 进程分别占用 1.7GB 和 1.0GB。
|
||||
5. **编译时间分析**:日志中 `compile:` 字段显示 Webpack 编译每个路由需 2-26 秒,对比 Turbopack 应在 0.5-3 秒。
|
||||
|
||||
---
|
||||
|
||||
## 二、根因分析
|
||||
|
||||
### 根因 1(P0):模块级单例在 HMR 中丢失
|
||||
|
||||
Next.js dev 模式下,Webpack HMR 会重新执行被修改(或依赖链变化)的模块。模块级 `let` 变量在每次重新执行时被重置为初始值。
|
||||
|
||||
```typescript
|
||||
// 修复前 — 每次 HMR 重新执行时 _db 重置为 null
|
||||
let _db: SqliteDatabase | null = null;
|
||||
|
||||
export function getDbInstance() {
|
||||
if (_db) return _db; // HMR 后这里永远 false
|
||||
// ... 重新打开一个新的 DB 连接(旧连接泄漏)
|
||||
}
|
||||
```
|
||||
|
||||
**受影响的模块与泄漏类型:**
|
||||
|
||||
| 模块 | 泄漏资源 | 累计次数 | 后果 |
|
||||
| ----------------------- | ---------------------- | -------- | ----------------------- |
|
||||
| `db/core.ts` | SQLite 连接 | 485 | 文件句柄泄漏 + 内存占用 |
|
||||
| `tokenHealthCheck.ts` | `setInterval` 定时器 | 586 | CPU 空转 + DB 查询风暴 |
|
||||
| `localHealthCheck.ts` | `setTimeout` 定时器链 | ~400 | 重复 HTTP 请求 + CPU |
|
||||
| `consoleInterceptor.ts` | console 方法包装 | ~400 | 日志 double-write |
|
||||
| `gracefulShutdown.ts` | SIGTERM/SIGINT handler | ~400 | 信号处理器堆叠 |
|
||||
|
||||
**级联效应**:泄漏的资源持续消耗内存和 CPU → 触发 Next.js 内存阈值保护 → 进程重启 → Webpack 从零重建模块图 → **Ready in 82.6s**。
|
||||
|
||||
### 根因 2(P0):强制使用 Webpack 而非 Turbopack
|
||||
|
||||
`scripts/run-next.mjs` 中硬编码了 `--webpack` 标志:
|
||||
|
||||
```javascript
|
||||
if (mode === "dev") {
|
||||
args.splice(2, 0, "--webpack");
|
||||
}
|
||||
```
|
||||
|
||||
Next.js 16 默认使用 Turbopack(Rust 编写的增量打包器),dev 编译速度是 Webpack 的 5-10 倍。强制回退到 Webpack 导致:
|
||||
|
||||
| 指标 | Webpack | Turbopack(预期) |
|
||||
| ----------------------- | ------- | ----------------- |
|
||||
| 首页编译 | 3.7s | ~0.5s |
|
||||
| Provider 详情页首次编译 | 22s | ~2-3s |
|
||||
| API route 首次编译 | 2-7s | ~0.3-1s |
|
||||
| 内存重启后 Ready | 82.6s | 不会触发 |
|
||||
|
||||
### 根因 3(P1):`node:crypto` 被拉入客户端 bundle
|
||||
|
||||
`src/lib/db/proxies.ts` 使用了 `import { randomUUID } from "node:crypto"`。通过 `localDb.ts` 的 re-export 链,这个 Node.js 原生模块被间接拉入客户端组件的 bundle,导致 Webpack 报错:
|
||||
|
||||
```
|
||||
UnhandledSchemeError: Reading from "node:crypto" is not handled by plugins
|
||||
Import trace: node:crypto → ./src/lib/db/proxies.ts → ./src/lib/localDb.ts → page.tsx
|
||||
```
|
||||
|
||||
Webpack 无法处理 `node:` URI scheme 前缀。`crypto`(不带 `node:` 前缀)已在 `next.config.mjs` 的 `serverExternalPackages` 中声明为服务端外部包。
|
||||
|
||||
### 根因 4(P1):Edge Runtime 编译警告刷屏
|
||||
|
||||
Next.js 16 会同时为 **Node.js** 和 **Edge** 两种运行时编译 `instrumentation.ts`。虽然 `register()` 函数内有 `process.env.NEXT_RUNTIME === "nodejs"` 的运行时守卫,但 Turbopack 在打包 Edge 版本时仍会**静态追踪**所有动态 `import()` 的依赖链:
|
||||
|
||||
```
|
||||
instrumentation.ts
|
||||
→ import("@/lib/db/secrets")
|
||||
→ @/lib/db/core.ts → fs, path, better-sqlite3
|
||||
→ @/lib/dataPaths.ts → path, os
|
||||
→ @/lib/db/migrationRunner.ts → fs, path, url
|
||||
```
|
||||
|
||||
对每个 Node.js 原生模块,Turbopack 都输出一条 "not supported in Edge Runtime" 警告。每次有新请求触发热编译时,这组 **10+ 条警告重复刷一遍**,严重污染终端输出,干扰开发调试。
|
||||
|
||||
### 根因 5(P2):启动 import 完全串行
|
||||
|
||||
`instrumentation.ts` 中 9 个 `await import()` 完全串行执行,每个都可能触发 Webpack 编译其依赖树:
|
||||
|
||||
```typescript
|
||||
await ensureSecrets(); // 串行 1
|
||||
const { initConsoleInterceptor } = await import(...); // 串行 2
|
||||
const { initGracefulShutdown } = await import(...); // 串行 3
|
||||
const { initApiBridgeServer } = await import(...); // 串行 4
|
||||
const { startBackgroundRefresh } = await import(...); // 串行 5
|
||||
const { getSettings } = await import(...); // 串行 6
|
||||
const { setCustomAliases } = await import(...); // 串行 7
|
||||
const { setDefaultFastServiceTierEnabled } = await import(...); // 串行 8
|
||||
const { initAuditLog, cleanupExpiredLogs } = await import(...); // 串行 9
|
||||
```
|
||||
|
||||
其中 4-6 互不依赖,7-8 互不依赖,完全可以并行。
|
||||
|
||||
---
|
||||
|
||||
## 三、修复方案
|
||||
|
||||
### 修复 1:globalThis 单例守卫(core.ts, tokenHealthCheck.ts, localHealthCheck.ts, consoleInterceptor.ts, gracefulShutdown.ts)
|
||||
|
||||
**原理**:`globalThis` 对象在 Node.js 进程生命周期内全局唯一,不受 Webpack 模块重新执行的影响。
|
||||
|
||||
```typescript
|
||||
// 修复后 — globalThis 在 HMR 后依然保留
|
||||
declare global {
|
||||
var __omnirouteDb: import("better-sqlite3").Database | undefined;
|
||||
}
|
||||
|
||||
function getDb() {
|
||||
return globalThis.__omnirouteDb ?? null;
|
||||
}
|
||||
function setDb(db) {
|
||||
/* ... */
|
||||
}
|
||||
|
||||
export function getDbInstance() {
|
||||
const existing = getDb();
|
||||
if (existing) return existing; // HMR 后命中缓存
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
**每个模块的具体改动:**
|
||||
|
||||
| 模块 | globalThis key | 守卫内容 |
|
||||
| ----------------------- | ----------------------------------- | ----------------------------------------------------------- |
|
||||
| `db/core.ts` | `__omnirouteDb` | SQLite 连接实例 |
|
||||
| `tokenHealthCheck.ts` | `__omnirouteTokenHC` | `{ initialized, interval }` |
|
||||
| `localHealthCheck.ts` | `__omnirouteLocalHC` | `{ initialized, sweepTimer, healthCache, sweepInProgress }` |
|
||||
| `consoleInterceptor.ts` | `__omnirouteConsoleInterceptorInit` | `boolean` |
|
||||
| `gracefulShutdown.ts` | `__omnirouteShutdownInit` | `boolean` |
|
||||
|
||||
**优点**:
|
||||
|
||||
- 零依赖,无需额外库。
|
||||
- 与 `apiBridgeServer.ts` 已有模式一致。
|
||||
- 对生产环境零影响(非 HMR 场景下行为完全相同)。
|
||||
|
||||
**缺点/注意**:
|
||||
|
||||
- `globalThis` 键名需全局唯一,使用 `__omniroute` 前缀避免冲突。
|
||||
- 需要 `declare global` 类型声明以保持 TypeScript 类型安全。
|
||||
- 生产构建中 `globalThis` 存储略冗余(但仅是一个对象引用,几乎零开销)。
|
||||
|
||||
### 修复 2:支持通过环境变量切换 Turbopack(run-next.mjs)
|
||||
|
||||
```javascript
|
||||
// 修复后 — 默认仍用 webpack(保持原有行为),设置环境变量可启用 Turbopack
|
||||
if (mode === "dev" && process.env.OMNIROUTE_USE_TURBOPACK !== "1") {
|
||||
args.splice(2, 0, "--webpack");
|
||||
}
|
||||
```
|
||||
|
||||
**默认行为不变**:dev 模式仍使用 Webpack,与修复前完全一致。设置 `OMNIROUTE_USE_TURBOPACK=1` 可切换到 Turbopack 以获得更快的 dev 编译速度。
|
||||
|
||||
**优点**:
|
||||
|
||||
- 零风险:不改变任何人的现有体验。
|
||||
- 需要时设置 `OMNIROUTE_USE_TURBOPACK=1` 即可获得 5-10 倍编译加速。
|
||||
- `next.config.mjs` 中已有 `turbopack.resolveAlias` 配置,说明项目已在准备 Turbopack 迁移。
|
||||
|
||||
**缺点/注意**:
|
||||
|
||||
- Turbopack 对某些 Webpack 特定配置(如自定义 externals 函数)的支持方式不同,启用前需测试兼容性。
|
||||
- 默认走 Webpack 意味着不主动启用 Turbopack 的用户无法享受编译加速。
|
||||
|
||||
### 修复 3:`node:crypto` → `crypto`(proxies.ts, errorResponse.ts)
|
||||
|
||||
```typescript
|
||||
// 修复前
|
||||
import { randomUUID } from "node:crypto";
|
||||
|
||||
// 修复后
|
||||
import { randomUUID } from "crypto";
|
||||
```
|
||||
|
||||
**优点**:
|
||||
|
||||
- `crypto`(无 `node:` 前缀)已在 `next.config.mjs` 的 `serverExternalPackages` 列表中,Webpack/Turbopack 会正确将其标记为外部包。
|
||||
- 消除 `UnhandledSchemeError` 构建失败。
|
||||
- Node.js 中 `crypto` 和 `node:crypto` 解析到同一模块。
|
||||
|
||||
**缺点**:
|
||||
|
||||
- 无。`crypto` 是 Node.js 内建模块,两种写法功能完全等价。
|
||||
|
||||
### 修复 4:分离 Edge/Node.js Instrumentation(instrumentation.ts → instrumentation-node.ts)
|
||||
|
||||
**问题**:`instrumentation.ts` 中所有 Node.js 逻辑(`ensureSecrets`、DB 初始化、审计日志等)虽然只在 `NEXT_RUNTIME === "nodejs"` 时执行,但 Turbopack 编译 Edge 版本时仍静态追踪其 import 链,对每个 `fs`/`path`/`os`/`better-sqlite3` 等原生模块输出警告。
|
||||
|
||||
**方案**:将所有 Node.js 专属逻辑提取到 `src/instrumentation-node.ts`,主文件通过**计算的 import 路径**引入,阻止 Turbopack 静态解析:
|
||||
|
||||
```typescript
|
||||
// src/instrumentation.ts — 精简后仅 ~20 行
|
||||
export async function register() {
|
||||
if (process.env.NEXT_RUNTIME === "nodejs") {
|
||||
// 拼接路径阻止 Turbopack 在 Edge 编译时静态解析模块依赖
|
||||
const nodeMod = "./instrumentation-" + "node";
|
||||
const { registerNodejs } = await import(nodeMod);
|
||||
await registerNodejs();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```typescript
|
||||
// src/instrumentation-node.ts — 包含全部 Node.js 启动逻辑
|
||||
export async function registerNodejs(): Promise<void> {
|
||||
await ensureSecrets();
|
||||
// initConsoleInterceptor, initGracefulShutdown, initApiBridgeServer, ...
|
||||
// (原 instrumentation.ts 的完整 Node.js 逻辑)
|
||||
}
|
||||
```
|
||||
|
||||
**关键技术**:`"./instrumentation-" + "node"` 是运行时拼接的字符串,Turbopack 无法在编译期确定其值,因此**不会追踪**该 import 的依赖树。Node.js 运行时则正常解析该路径并执行。
|
||||
|
||||
**优点**:
|
||||
|
||||
- Edge 编译时完全跳过 Node.js 模块追踪,**10+ 条重复警告全部消除**。
|
||||
- Node.js 运行时行为与修复前完全一致。
|
||||
- 启动时间从 **13.9s → 1.25s**(Turbopack 不再在 Edge 编译中处理 Node.js 模块图)。
|
||||
|
||||
**缺点/注意**:
|
||||
|
||||
- 新增一个文件 `instrumentation-node.ts`,需同步维护。
|
||||
- 计算 import 路径是有意为之的 bundler 逃逸技巧,需加注释说明原因防止后续重构时被"优化"回静态字符串。
|
||||
|
||||
### 修复 5:并行化 instrumentation.ts 中的启动 import
|
||||
|
||||
```typescript
|
||||
// 修复后 — 4 个独立模块并行导入
|
||||
const [
|
||||
{ initGracefulShutdown },
|
||||
{ initApiBridgeServer },
|
||||
{ startBackgroundRefresh },
|
||||
{ getSettings },
|
||||
] = await Promise.all([
|
||||
import("@/lib/gracefulShutdown"),
|
||||
import("@/lib/apiBridgeServer"),
|
||||
import("@/domain/quotaCache"),
|
||||
import("@/lib/db/settings"),
|
||||
]);
|
||||
|
||||
// 2 个 open-sse 模块也并行导入
|
||||
const [{ setCustomAliases }, { setDefaultFastServiceTierEnabled }] = await Promise.all([
|
||||
import("@omniroute/open-sse/services/modelDeprecation.ts"),
|
||||
import("@omniroute/open-sse/executors/codex.ts"),
|
||||
]);
|
||||
```
|
||||
|
||||
**优点**:
|
||||
|
||||
- `consoleInterceptor` 仍保持第一个(必须在任何日志前初始化)。
|
||||
- 后续 4 个无依赖模块并行加载,节省 3 次串行等待。
|
||||
- open-sse 的 2 个模块也并行加载。
|
||||
|
||||
**缺点**:
|
||||
|
||||
- 并行 import 的错误堆栈略复杂(Promise.all 中某一个失败会 reject 整个组)。
|
||||
- 这里的 compliance 模块仍保持独立 try/catch 串行,因为它有自己的错误处理逻辑。
|
||||
|
||||
---
|
||||
|
||||
## 四、预期效果
|
||||
|
||||
| 指标 | 修复前 | 修复后(预期) |
|
||||
| ----------------------------- | ------------------------- | ------------------------ |
|
||||
| DB 连接创建次数 | 485 次/会话 | 1 次 |
|
||||
| HealthCheck 定时器 | 586 个泄漏 | 1 个 |
|
||||
| 信号处理器注册 | ~400 次重复 | 1 次 |
|
||||
| Console 拦截层数 | ~400 层嵌套 | 1 层 |
|
||||
| 内存使用峰值 | 2.4 GB → OOM 重启 | 预期 < 500 MB |
|
||||
| 冷启动 Ready | 3.4s | ~3s(略快) |
|
||||
| 内存重启 Ready | 82.6s | 不再触发内存重启 |
|
||||
| Login 页首次编译 | 3.7s | ~0.5s (需启用 Turbopack) |
|
||||
| Provider 详情页首次编译 | 22s | ~2-3s (需启用 Turbopack) |
|
||||
| `node:crypto` 构建错误 | 反复出现 | 消除 |
|
||||
| Edge Runtime 编译警告 | 每次热编译刷出 10+ 条 | **0 条** |
|
||||
| instrumentation 启动耗时 | 13.9s(含 Edge 模块追踪) | **1.25s** |
|
||||
| instrumentation import 并行度 | 9 次串行 import | 3 批并行 import |
|
||||
|
||||
---
|
||||
|
||||
## 五、涉及文件清单
|
||||
|
||||
| 区域 | 文件 | 改动类型 |
|
||||
| ------------------- | ------------------------------- | ------------------------------------------------------------------ |
|
||||
| DB 单例 | `src/lib/db/core.ts` | `let _db` → `globalThis.__omnirouteDb` |
|
||||
| Token 健康检查 | `src/lib/tokenHealthCheck.ts` | `let initialized` → `globalThis.__omnirouteTokenHC` |
|
||||
| 本地节点健康检查 | `src/lib/localHealthCheck.ts` | `let initialized` → `globalThis.__omnirouteLocalHC` |
|
||||
| Console 拦截 | `src/lib/consoleInterceptor.ts` | `let initialized` → `globalThis.__omnirouteConsoleInterceptorInit` |
|
||||
| 优雅关停 | `src/lib/gracefulShutdown.ts` | 新增 `globalThis.__omnirouteShutdownInit` 守卫 |
|
||||
| Dev 启动脚本 | `scripts/run-next.mjs` | 新增 `OMNIROUTE_USE_TURBOPACK=1` 开关 |
|
||||
| Proxy 注册表 | `src/lib/db/proxies.ts` | `node:crypto` → `crypto` |
|
||||
| API 错误响应 | `src/lib/api/errorResponse.ts` | `node:crypto` → `crypto` |
|
||||
| 启动钩子(主入口) | `src/instrumentation.ts` | 精简为 ~20 行,计算 import 路径阻止 Edge 追踪 |
|
||||
| 启动钩子(Node.js) | `src/instrumentation-node.ts` | 新文件,承载全部 Node.js 启动逻辑 + `Promise.all` 并行 |
|
||||
|
||||
---
|
||||
|
||||
## 六、回退方案
|
||||
|
||||
- **启用 Turbopack**:设置 `OMNIROUTE_USE_TURBOPACK=1` 环境变量;不设置则默认使用 Webpack(原有行为不变)。
|
||||
- **globalThis 方案异常**:所有 globalThis key 都以 `__omniroute` 为前缀,可通过 `delete globalThis.__omnirouteDb` 等方式手动重置。
|
||||
- **Edge 警告回退**:若 `instrumentation-node.ts` 拆分导致问题,可将其内容合并回 `instrumentation.ts`,恢复为直接 `import()` 调用(警告会重新出现但不影响功能)。
|
||||
- **生产环境**:以上修复对生产构建无负面影响——生产环境不存在 HMR,globalThis 单例仅在首次调用时初始化一次。计算 import 路径在 `next build` 时由 Node.js 正常解析,不影响打包产物。
|
||||
|
||||
---
|
||||
|
||||
## 七、单元测试与备份恢复(pre-commit 验证通过)
|
||||
|
||||
为保证提交前必须通过验证(不再使用 `--no-verify`),对以下失败用例与生产逻辑做了修复与加固。
|
||||
|
||||
### 问题与根因
|
||||
|
||||
| 失败项 | 根因 |
|
||||
| ------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| bootstrap-env 4 个用例 | Windows 上 DATA_DIR 解析用 `APPDATA`/`homedir()`,测试只设了 `HOME`,脚本读不到测试用的 `.env`。 |
|
||||
| domain-persistence costRules 2 个用例 | `core` 在首次 import 时缓存 `DATA_DIR`;测试每测一个 tmpDir 并在 afterEach 删目录,导致后续 describe 使用的 DB 路径已被删,读写得到 0。 |
|
||||
| fixes-p1 restoreDbBackup | 测试在 DB 仍打开时写 stale 侧文件;`restoreDbBackup` 内 pre-restore 备份未 await 就关库,Windows 上句柄未及时释放,unlink 报 EBUSY。 |
|
||||
| fixes-p1 resetStorage 及后续用例 | 上一测留下 DB 打开,下一测 `resetStorage()` 删目录时文件仍被占用,EBUSY。 |
|
||||
|
||||
### 修复 6:bootstrap-env 测试(tests/unit/bootstrap-env.test.mjs)
|
||||
|
||||
在每个用例的 `withTempEnv` 回调开头增加 `process.env.DATA_DIR = dataDir`,使脚本在任意平台(含 Windows)都使用测试临时目录,而不是依赖 `HOME`/`APPDATA`。
|
||||
|
||||
### 修复 7:domain-persistence 测试(tests/unit/domain-persistence.test.mjs)
|
||||
|
||||
- **单例 tmpDir**:全文件共用一个 `fileTmpDir`,在模块加载时创建并设置 `process.env.DATA_DIR`,与 `core` 首次加载时缓存的路径一致。
|
||||
- **每测清 DB 不清目录**:`beforeEach` 中 `resetDbInstance()` 后删除 `storage.sqlite` 及其 `-wal`/`-shm`/`-journal`,保证每测干净 DB,不在 afterEach 删目录,避免路径失效。
|
||||
- **收尾**:`after()` 中恢复 `DATA_DIR` 并删除 `fileTmpDir`。
|
||||
- **costRules 断言**:改为小容差精确校验(`assertAlmostEqual`),继续验证 `4.5` / `4.0` 这类业务关键值,避免把真实累计错误放过去。
|
||||
|
||||
### 修复 8:fixes-p1 测试(tests/unit/fixes-p1.test.mjs)
|
||||
|
||||
- **restoreDbBackup 用例**:在写入 stale 侧文件前调用 `core.resetDbInstance()`,避免 DB 仍打开时写 `-wal`/`-shm` 触发 Windows 锁错误。
|
||||
- **Windows 跳过**:该用例在 Windows 上仍使用 `test(..., { skip: isWindows })`。原因不是业务逻辑不支持 Windows,而是 better-sqlite3 关闭后底层句柄释放存在时序抖动,这条真实 sidecar 集成测试容易退化成不稳定的文件锁测试;Linux/macOS 上照常运行。
|
||||
- **核心兜底测试**:新增平台无关的 `unlinkFileWithRetry` 单测,直接模拟 `EBUSY` / `EPERM` 后重试并最终成功,确保 Windows 相关的重试删除逻辑被稳定覆盖,而不是完全依赖 flaky 的真实文件锁时序。
|
||||
- **resetStorage**:改为 async,对 `rmSync(TEST_DATA_DIR)` 做最多 10 次、间隔 100ms 的 EBUSY/EPERM 重试,避免下一测因上一测句柄未释放而失败。
|
||||
|
||||
### 修复 9:备份恢复逻辑(src/lib/db/backup.ts)
|
||||
|
||||
- **pre-restore 备份改为同步等待**:在 `restoreDbBackup` 内用内联逻辑做 pre-restore 备份并 `await` 完成,再调用 `resetDbInstance()`,避免异步 backup 未结束就关库导致后续 unlink 失败。
|
||||
- **节流语义保持一致**:pre-restore 备份成功后补回 `_lastBackupAt = Date.now()`,避免恢复后紧接着又触发一轮额外自动备份。
|
||||
- **关库后短延迟**:`resetDbInstance()` 后 `await new Promise(r => setTimeout(r, 500))`,再执行 unlink,给 Windows 等平台释放句柄留时间。
|
||||
- **unlink 重试**:将主库及 `-wal`/`-shm`/`-journal` 的删除提取为 `unlinkFileWithRetry`,统一做最多 10 次、间隔 100ms 的 EBUSY/EPERM 重试,提高恢复流程在锁释放较慢环境下的成功率,也便于单测直接覆盖重试逻辑。
|
||||
|
||||
### 涉及文件(本节)
|
||||
|
||||
| 区域 | 文件 | 改动类型 |
|
||||
| -------- | ---------------------------------------- | --------------------------------------------------------------------------------------------------------------- |
|
||||
| 单元测试 | `tests/unit/bootstrap-env.test.mjs` | 各用例内设置 `process.env.DATA_DIR = dataDir` |
|
||||
| 单元测试 | `tests/unit/domain-persistence.test.mjs` | 单例 tmpDir、beforeEach 清 DB 文件、after 删目录;costRules 改为小容差精确断言 |
|
||||
| 单元测试 | `tests/unit/fixes-p1.test.mjs` | restoreDbBackup 前 resetDbInstance、Windows skip 说明、resetStorage 重试、`unlinkFileWithRetry` 核心单测 |
|
||||
| 备份恢复 | `src/lib/db/backup.ts` | pre-restore 内联并 await、恢复 `_lastBackupAt` 节流语义、关库后 500ms 延迟、抽取 `unlinkFileWithRetry` 重试删除 |
|
||||
@@ -1,332 +0,0 @@
|
||||
# ZWS_README_V5 — 按协议配置模型兼容性 + 前端性能优化
|
||||
|
||||
V4 内容(HMR 泄漏修复、Edge 警告消除、测试稳定性)已完成;V5 在 V4 基础上实现**按协议维度配置模型兼容性**,新增前端查找性能优化与类型安全改进。
|
||||
|
||||
---
|
||||
|
||||
## 一、如何发现问题
|
||||
|
||||
### 现象
|
||||
|
||||
- 同一模型被 **OpenAI Chat Completions**、**OpenAI Responses API**、**Anthropic Messages** 三种客户端请求形态调用时,V2 的兼容性开关(工具 ID 9 位、不保留 developer 角色)是**全局生效**的——无法为不同协议设置不同的兼容策略。
|
||||
- 例如:用户希望 OpenAI Responses API 请求时不保留 developer 角色(MiniMax 422 修复),但 OpenAI Chat Completions 请求时保留。V2 下只能二选一。
|
||||
- 前端兼容性弹层未标明当前配置对应哪种协议,容易误导。
|
||||
- 前端组件中 `Array.find()` 在每次渲染时对 customModels 和 modelCompatOverrides 做 O(n) 线性扫描,模型数量多时存在不必要的性能开销。
|
||||
- `ModelCompatPatch` 类型定义与运行时逻辑不一致:`preserveOpenAIDeveloperRole` 字段需要支持 `null`(表示取消设置/恢复默认),但类型仅允许 `boolean`。
|
||||
|
||||
### 排查过程
|
||||
|
||||
1. **需求分析**:梳理 `detectFormat(body)` 返回的三种协议键(`openai`、`openai-responses`、`claude`),确认每种协议对 developer 角色和 tool call ID 的需求不同。
|
||||
2. **数据模型设计**:在现有 `normalizeToolCallId` / `preserveOpenAIDeveloperRole` 顶层字段基础上,设计 `compatByProtocol` 嵌套结构,按协议键细分。
|
||||
3. **构建问题**:客户端 `"use client"` 组件直接从 `@/lib/localDb` 引入常量时,间接拉入了 `node:crypto`(经由 `db/proxies.ts`),触发 Webpack `UnhandledSchemeError`。需将常量拆到 `shared/` 层。
|
||||
4. **前端性能**:通过 React DevTools 和代码审计发现 `effectiveNormalizeForProtocol` 等函数每次调用都对数组做 `find()`,在渲染列表时存在 O(n²) 的隐患。
|
||||
|
||||
---
|
||||
|
||||
## 二、根因分析
|
||||
|
||||
### 根因 1(P0):兼容选项无协议维度
|
||||
|
||||
V2 的 `normalizeToolCallId` / `preserveOpenAIDeveloperRole` 存储在模型级别的顶层字段,无法区分请求来源协议。`chatCore.ts` 中的 getter 函数只接收 `(providerId, modelId)` 两个参数,不感知当前请求的 `sourceFormat`。
|
||||
|
||||
**影响**:跨协议场景下用户只能设置一个全局值,无法精确控制。
|
||||
|
||||
### 根因 2(P1):客户端构建拉入 Node.js 模块
|
||||
|
||||
`page.tsx`("use client")→ `@/lib/localDb` → `db/proxies.ts` → `import { randomUUID } from "node:crypto"`
|
||||
|
||||
Webpack 无法处理 `node:` URI scheme,报 `UnhandledSchemeError`。虽然 V4 已将 `node:crypto` → `crypto` 修复了 `proxies.ts`,但 `localDb.ts` 的 barrel export 链仍然存在风险——客户端组件不应引入任何可能传递到 Node.js 模块的路径。
|
||||
|
||||
### 根因 3(P2):前端查找性能
|
||||
|
||||
`effectiveNormalizeForProtocol`、`effectivePreserveForProtocol`、`anyNormalizeCompatBadge`、`anyNoPreserveCompatBadge` 四个函数每次调用都使用 `Array.find()` 在 `customModels` 和 `modelCompatOverrides` 数组中查找目标模型。在模型列表渲染时,每个模型行会调用多次这些函数,导致 O(n × m) 的查找开销(n = 模型数,m = 每行调用次数)。
|
||||
|
||||
### 根因 4(P2):类型定义与运行时不一致
|
||||
|
||||
```typescript
|
||||
// V3 暂存区版本(有问题)
|
||||
export type ModelCompatPatch = Partial<
|
||||
Pick<
|
||||
ModelCompatOverride,
|
||||
"normalizeToolCallId" | "preserveOpenAIDeveloperRole" | "compatByProtocol"
|
||||
>
|
||||
>;
|
||||
```
|
||||
|
||||
`ModelCompatOverride.preserveOpenAIDeveloperRole` 类型为 `boolean | undefined`,但 `mergeModelCompatOverride()` 内部有 `=== null` 判断(用于取消设置/恢复默认),类型层面无法覆盖。
|
||||
|
||||
---
|
||||
|
||||
## 三、修复方案
|
||||
|
||||
### 修复 1:`compatByProtocol` 存储与读取(models.ts)
|
||||
|
||||
**新增数据结构**:
|
||||
|
||||
```typescript
|
||||
type CompatByProtocolMap = Partial<Record<ModelCompatProtocolKey, ModelCompatPerProtocol>>;
|
||||
|
||||
export type ModelCompatOverride = {
|
||||
id: string;
|
||||
normalizeToolCallId?: boolean;
|
||||
preserveOpenAIDeveloperRole?: boolean;
|
||||
compatByProtocol?: CompatByProtocolMap; // 新增
|
||||
};
|
||||
```
|
||||
|
||||
**读取优先级链**(适用于 `getModelNormalizeToolCallId` 和 `getModelPreserveOpenAIDeveloperRole`):
|
||||
|
||||
```
|
||||
compatByProtocol[sourceFormat].field → 顶层 field → 默认值
|
||||
```
|
||||
|
||||
1. 若 `sourceFormat` 属于已知协议键(`openai` / `openai-responses` / `claude`),且 `compatByProtocol[sourceFormat]` 中存在目标字段,使用该值。
|
||||
2. 否则回退到顶层字段。
|
||||
3. 顶层字段也不存在时使用默认值(normalizeToolCallId=false,preserveOpenAIDeveloperRole=undefined)。
|
||||
|
||||
**深度合并逻辑** `deepMergeCompatByProtocol()`:
|
||||
|
||||
- 对每个协议键,逐字段合并而非覆盖。
|
||||
- `normalizeToolCallId=false` 时删除该字段(不存储 false,减少冗余)。
|
||||
- 合并后若整个协议条目为空对象,删除该协议条目。
|
||||
- 协议键通过 `isCompatProtocolKey()` 白名单校验,拒绝未知键。
|
||||
|
||||
**Getter 签名扩展**(向后兼容,第三参数可选):
|
||||
|
||||
```typescript
|
||||
export function getModelNormalizeToolCallId(
|
||||
providerId: string,
|
||||
modelId: string,
|
||||
sourceFormat?: string | null
|
||||
): boolean;
|
||||
|
||||
export function getModelPreserveOpenAIDeveloperRole(
|
||||
providerId: string,
|
||||
modelId: string,
|
||||
sourceFormat?: string | null
|
||||
): boolean | undefined;
|
||||
```
|
||||
|
||||
**优点**:
|
||||
|
||||
- 完全向后兼容:无 `sourceFormat` 参数时行为与 V2 完全一致。
|
||||
- 协议键白名单校验防止存储污染。
|
||||
- 深度合并保留未变更协议的配置。
|
||||
|
||||
**缺点/注意**:
|
||||
|
||||
- JSON 存储体积略增(每个模型最多增加 3 个协议条目)。
|
||||
- 新增 ~80 行 TypeScript 代码。
|
||||
|
||||
### 修复 2:请求管线传入 sourceFormat(chatCore.ts)
|
||||
|
||||
```typescript
|
||||
const normalizeToolCallId = getModelNormalizeToolCallId(
|
||||
provider || "",
|
||||
model || "",
|
||||
sourceFormat // 新增第三参
|
||||
);
|
||||
const preserveDeveloperRole = getModelPreserveOpenAIDeveloperRole(
|
||||
provider || "",
|
||||
model || "",
|
||||
sourceFormat // 新增第三参
|
||||
);
|
||||
```
|
||||
|
||||
`sourceFormat` 由已有的 `detectFormat(body)` 返回,无需新增检测逻辑。
|
||||
|
||||
**优点**:
|
||||
|
||||
- 改动仅 2 行,精准传参。
|
||||
- 不影响其他 handler(embeddings、imageGeneration 等不涉及 developer 角色和 tool call ID)。
|
||||
|
||||
### 修复 3:API 路由支持 compatByProtocol(route.ts)
|
||||
|
||||
**PUT 请求体扩展**:
|
||||
|
||||
- 解构 `compatByProtocol` 并传入 `updateCustomModel()`。
|
||||
- `compatOnly` 判断扩展:仅含 `provider` + `modelId` + 兼容字段时,走 `mergeModelCompatOverride()` 路径。
|
||||
- 使用 `ModelCompatPatch` 类型替代行内类型定义,统一类型来源。
|
||||
|
||||
**Zod 校验 schema**:
|
||||
|
||||
```typescript
|
||||
const modelCompatPerProtocolSchema = z.object({
|
||||
normalizeToolCallId: z.boolean().optional(),
|
||||
preserveOpenAIDeveloperRole: z.boolean().optional(),
|
||||
}).strict(); // strict: 拒绝额外字段
|
||||
|
||||
compatByProtocol: z
|
||||
.record(z.enum(["openai", "openai-responses", "claude"]), modelCompatPerProtocolSchema)
|
||||
.optional(),
|
||||
```
|
||||
|
||||
**优点**:
|
||||
|
||||
- `.strict()` 防止客户端注入额外字段。
|
||||
- `z.enum()` 限定协议键,与后端白名单一致。
|
||||
- 仅传 `compatByProtocol` 即可更新,前端无需拼装完整模型对象。
|
||||
|
||||
### 修复 4:客户端安全常量拆分(modelCompat.ts)
|
||||
|
||||
**新增** `src/shared/constants/modelCompat.ts`:
|
||||
|
||||
```typescript
|
||||
export const MODEL_COMPAT_PROTOCOL_KEYS = ["openai", "openai-responses", "claude"] as const;
|
||||
export type ModelCompatProtocolKey = (typeof MODEL_COMPAT_PROTOCOL_KEYS)[number];
|
||||
```
|
||||
|
||||
- 不依赖 Node.js / DB 代码,客户端组件可安全引入。
|
||||
- `models.ts` 从此模块引入并再导出。
|
||||
- `localDb.ts` 新增 `ModelCompatPatch` 类型导出(供 route.ts 使用),不导出协议常量。
|
||||
- `page.tsx` 改为从 `@/shared/constants/modelCompat` 引入。
|
||||
|
||||
**优点**:
|
||||
|
||||
- 彻底切断客户端 → localDb → db → proxies → node:crypto 的依赖链。
|
||||
- 协议键定义单一来源(Single Source of Truth)。
|
||||
|
||||
### 修复 5:前端协议选择器与按协议解析(page.tsx)
|
||||
|
||||
**ModelCompatPopover 重构**:
|
||||
|
||||
- 新增协议下拉选择器(`<select>`),可选 OpenAI Chat / OpenAI Responses / Anthropic Messages。
|
||||
- 两个开关(工具 ID 9 位、不保留 developer)**针对选中协议**生效。
|
||||
- 选择 Claude 协议时隐藏 developer 角色开关(developer 仅对 OpenAI 系有意义)。
|
||||
- 保存时以 `{ compatByProtocol: { [protocol]: payload } }` 形式提交,后端按协议合并。
|
||||
- 深色模式适配:下拉框使用 `bg-white dark:bg-zinc-800`、`text-zinc-900 dark:text-zinc-100`。
|
||||
|
||||
**Props 接口重构**:
|
||||
|
||||
旧接口(4 个独立值/回调):
|
||||
|
||||
```typescript
|
||||
(normalizeToolCallId, preserveDeveloperRole, onNormalizeChange, onPreserveChange);
|
||||
```
|
||||
|
||||
新接口(3 个函数式 props):
|
||||
|
||||
```typescript
|
||||
effectiveModelNormalize: (protocol: string) => boolean
|
||||
effectiveModelPreserveDeveloper: (protocol: string) => boolean
|
||||
onCompatPatch: (protocol: string, payload: {...}) => void
|
||||
```
|
||||
|
||||
所有消费方(`ModelRow`、`PassthroughModelRow`、`CustomModelsSection`、`CompatibleModelsSection`)已同步更新。
|
||||
|
||||
**角标显示逻辑**:
|
||||
|
||||
- `anyNormalizeCompatBadge()`:任意协议或顶层存在 `normalizeToolCallId=true` 即显示「ID×9」角标。
|
||||
- `anyNoPreserveCompatBadge()`:任意协议或顶层存在 `preserveOpenAIDeveloperRole=false` 即显示「不保留」角标。
|
||||
|
||||
**CustomModelsSection 增强**:
|
||||
|
||||
- 新增 `modelCompatOverrides` 状态,从 API 响应中获取。
|
||||
- 新增 `saveCustomCompat()` 函数,支持仅传 `compatByProtocol` 的独立保存。
|
||||
|
||||
### 修复 6:前端 Map 查找性能优化(page.tsx)
|
||||
|
||||
**问题**:`effectiveNormalizeForProtocol` 等函数对 `customModels` 和 `modelCompatOverrides` 用 `Array.find()` 做 O(n) 查找,在列表渲染时每个模型行多次调用。
|
||||
|
||||
**方案**:使用 `useMemo` + `Map` 将数组预建为 O(1) 查找表。
|
||||
|
||||
```typescript
|
||||
type CompatModelMap = Map<string, CompatModelRow>;
|
||||
|
||||
function buildCompatMap(rows: CompatModelRow[]): CompatModelMap {
|
||||
const m = new Map<string, CompatModelRow>();
|
||||
for (const r of rows) if (r.id) m.set(r.id, r);
|
||||
return m;
|
||||
}
|
||||
|
||||
// 在组件内
|
||||
const customMap = useMemo(() => buildCompatMap(modelMeta.customModels), [modelMeta.customModels]);
|
||||
const overrideMap = useMemo(
|
||||
() => buildCompatMap(modelMeta.modelCompatOverrides),
|
||||
[modelMeta.modelCompatOverrides]
|
||||
);
|
||||
```
|
||||
|
||||
所有查找函数签名从 `(modelId, protocol, customModels[], overrides[])` 改为 `(modelId, protocol, customMap, overrideMap)`,内部使用 `Map.get()` 替代 `Array.find()`。
|
||||
|
||||
**优点**:
|
||||
|
||||
- 查找从 O(n) 降为 O(1)。
|
||||
- `useMemo` 依赖项正确,仅在数据变化时重建 Map。
|
||||
- `CustomModelsSection` 内部也独立构建 Map,不依赖父组件。
|
||||
|
||||
### 修复 7:ModelCompatPatch 类型修正(models.ts)
|
||||
|
||||
```typescript
|
||||
// 修复后 — 显式允许 null
|
||||
export type ModelCompatPatch = {
|
||||
normalizeToolCallId?: boolean;
|
||||
preserveOpenAIDeveloperRole?: boolean | null; // null = 取消设置/恢复默认
|
||||
compatByProtocol?: CompatByProtocolMap;
|
||||
};
|
||||
```
|
||||
|
||||
与 `mergeModelCompatOverride()` 内的 `=== null` 判断逻辑一致,类型安全。
|
||||
|
||||
### 修复 8:CompatByProtocolMap 类型收紧(page.tsx)
|
||||
|
||||
客户端 `CompatByProtocolMap` 从 `Record<string, ...>` 改为 `Record<ModelCompatProtocolKey, ...>`,增强类型安全,防止传入未知协议键。
|
||||
|
||||
### 修复 9:i18n 文案新增
|
||||
|
||||
| 键名 | 中文 | 英文 |
|
||||
| ------------------------------- | --------------------------------------------- | -------------------------------------------------------------- |
|
||||
| `compatProtocolLabel` | 客户端请求协议 | Client request protocol |
|
||||
| `compatProtocolHint` | 以下选项在 OmniRoute 识别到该请求形态时生效。 | These options apply when OmniRoute detects this request shape. |
|
||||
| `compatProtocolOpenAI` | OpenAI Chat Completions | OpenAI Chat Completions |
|
||||
| `compatProtocolOpenAIResponses` | OpenAI Responses API | OpenAI Responses API |
|
||||
| `compatProtocolClaude` | Anthropic Messages | Anthropic Messages |
|
||||
|
||||
---
|
||||
|
||||
## 四、使用方式
|
||||
|
||||
1. 点击模型行的 **「兼容性」** 按钮。
|
||||
2. 在弹层内先选择 **「客户端请求协议」**(OpenAI Chat / OpenAI Responses / Anthropic Messages)。
|
||||
3. 勾选该协议下的「工具 ID 9 位」或「不保留 developer 角色」。
|
||||
4. 保存后,仅在该协议形态的请求下生效。
|
||||
5. 未配置某协议时,该协议下行为回退到顶层兼容字段(若存在),再回退到默认值(保留 developer、不规范化 tool id)。
|
||||
6. 角标「ID×9」「不保留」在任意协议存在对应配置时显示。
|
||||
|
||||
---
|
||||
|
||||
## 五、预期效果
|
||||
|
||||
| 指标 | 修复前 | 修复后 |
|
||||
| ------------------------- | --------------------- | ------------------------------------------ |
|
||||
| 兼容性配置维度 | 全局(模型级) | 按协议(OpenAI Chat / Responses / Claude) |
|
||||
| developer 角色精确控制 | 不支持 | 支持(如:仅 Responses API 不保留) |
|
||||
| 前端兼容性查找性能 | O(n) Array.find | O(1) Map.get(useMemo 缓存) |
|
||||
| ModelCompatPatch 类型安全 | null 值无类型覆盖 | 显式 `boolean \| null` |
|
||||
| 客户端构建风险 | 可能引入 Node.js 模块 | 已隔离(shared/constants 层) |
|
||||
| API 验证 | 无 compatByProtocol | Zod strict schema 校验 |
|
||||
| 深色模式 | 协议选择器不可读 | bg/text 适配 dark 主题 |
|
||||
|
||||
---
|
||||
|
||||
## 六、涉及文件清单
|
||||
|
||||
| 区域 | 文件 | 改动类型 |
|
||||
| ---------- | ----------------------------------------------------------- | ------------------------------------------------------------------------------------------------ |
|
||||
| 协议常量 | `src/shared/constants/modelCompat.ts` | **新建**,客户端安全的协议键与类型 |
|
||||
| 存储与读写 | `src/lib/db/models.ts` | `compatByProtocol` 数据结构、深度合并、getter 第三参 `sourceFormat`、`ModelCompatPatch` 类型修正 |
|
||||
| 再导出层 | `src/lib/localDb.ts` | 新增 `ModelCompatPatch` 类型导出 |
|
||||
| API 路由 | `src/app/api/provider-models/route.ts` | PUT 支持 `compatByProtocol`,使用 `ModelCompatPatch` 类型 |
|
||||
| 输入校验 | `src/shared/validation/schemas.ts` | `modelCompatPerProtocolSchema`(strict)+ `compatByProtocol` 记录校验 |
|
||||
| 请求管线 | `open-sse/handlers/chatCore.ts` | `getModelNormalizeToolCallId` / `getModelPreserveOpenAIDeveloperRole` 传入 `sourceFormat` |
|
||||
| 前端 UI | `src/app/(dashboard)/dashboard/providers/[id]/page.tsx` | 协议选择器、按协议解析/保存、角标逻辑、Map 性能优化、类型收紧 |
|
||||
| i18n | `src/i18n/messages/en.json`,`src/i18n/messages/zh-CN.json` | 5 条新文案 |
|
||||
|
||||
---
|
||||
|
||||
## 七、回退方案
|
||||
|
||||
- **禁用按协议配置**:删除 `compatByProtocol` 字段后,getter 自动回退到顶层字段,行为与 V2 一致。
|
||||
- **前端 Map 优化回退**:将 `Map.get()` 改回 `Array.find()` 即可,纯性能优化无功能耦合。
|
||||
- **客户端常量回退**:将 `MODEL_COMPAT_PROTOCOL_KEYS` 定义移回 `models.ts` 并从 `localDb.ts` 导出(需同时确保 `node:crypto` 问题不再存在)。
|
||||
- **生产环境**:以上修复对生产构建无负面影响。`compatByProtocol` 为可选字段,未配置时默认行为不变。API Zod 校验确保不会接受畸形数据。
|
||||
@@ -142,10 +142,10 @@ The provider prefix is auto-added if missing. Mismatched models return `400`.
|
||||
|
||||
```bash
|
||||
# Get cache stats
|
||||
GET /api/cache
|
||||
GET /api/cache/stats
|
||||
|
||||
# Clear all caches
|
||||
DELETE /api/cache
|
||||
DELETE /api/cache/stats
|
||||
```
|
||||
|
||||
Response example:
|
||||
@@ -218,7 +218,7 @@ Response example:
|
||||
|
||||
| Endpoint | Method | Description |
|
||||
| ------------------------------- | ------- | ---------------------- |
|
||||
| `/api/settings` | GET/PUT | General settings |
|
||||
| `/api/settings` | GET/PUT/PATCH | General settings |
|
||||
| `/api/settings/proxy` | GET/PUT | Network proxy config |
|
||||
| `/api/settings/proxy/test` | POST | Test proxy connection |
|
||||
| `/api/settings/ip-filter` | GET/PUT | IP allowlist/blocklist |
|
||||
@@ -231,8 +231,8 @@ Response example:
|
||||
| ------------------------ | ---------- | ----------------------- |
|
||||
| `/api/sessions` | GET | Active session tracking |
|
||||
| `/api/rate-limits` | GET | Per-account rate limits |
|
||||
| `/api/monitoring/health` | GET | Health check |
|
||||
| `/api/cache` | GET/DELETE | Cache stats / clear |
|
||||
| `/api/monitoring/health` | GET | Health check + provider summary (`catalogCount`, `configuredCount`, `activeCount`, `monitoredCount`) |
|
||||
| `/api/cache/stats` | GET/DELETE | Cache stats / clear |
|
||||
|
||||
### Backup & Export/Import
|
||||
|
||||
@@ -279,7 +279,7 @@ GET response includes `agents[]` (id, name, binary, version, installed, protocol
|
||||
|
||||
| Endpoint | Method | Description |
|
||||
| ----------------------- | ------- | ------------------------------- |
|
||||
| `/api/resilience` | GET/PUT | Get/update resilience profiles |
|
||||
| `/api/resilience` | GET/PATCH | Get/update resilience profiles |
|
||||
| `/api/resilience/reset` | POST | Reset circuit breakers |
|
||||
| `/api/rate-limits` | GET | Per-account rate limit status |
|
||||
| `/api/rate-limit` | GET | Global rate limit configuration |
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
🌐 **Languages:** 🇺🇸 [English](ARCHITECTURE.md) | 🇧🇷 [Português (Brasil)](i18n/pt-BR/ARCHITECTURE.md) | 🇪🇸 [Español](i18n/es/ARCHITECTURE.md) | 🇫🇷 [Français](i18n/fr/ARCHITECTURE.md) | 🇮🇹 [Italiano](i18n/it/ARCHITECTURE.md) | 🇷🇺 [Русский](i18n/ru/ARCHITECTURE.md) | 🇨🇳 [中文 (简体)](i18n/zh-CN/ARCHITECTURE.md) | 🇩🇪 [Deutsch](i18n/de/ARCHITECTURE.md) | 🇮🇳 [हिन्दी](i18n/in/ARCHITECTURE.md) | 🇹🇭 [ไทย](i18n/th/ARCHITECTURE.md) | 🇺🇦 [Українська](i18n/uk-UA/ARCHITECTURE.md) | 🇸🇦 [العربية](i18n/ar/ARCHITECTURE.md) | 🇯🇵 [日本語](i18n/ja/ARCHITECTURE.md) | 🇻🇳 [Tiếng Việt](i18n/vi/ARCHITECTURE.md) | 🇧🇬 [Български](i18n/bg/ARCHITECTURE.md) | 🇩🇰 [Dansk](i18n/da/ARCHITECTURE.md) | 🇫🇮 [Suomi](i18n/fi/ARCHITECTURE.md) | 🇮🇱 [עברית](i18n/he/ARCHITECTURE.md) | 🇭🇺 [Magyar](i18n/hu/ARCHITECTURE.md) | 🇮🇩 [Bahasa Indonesia](i18n/id/ARCHITECTURE.md) | 🇰🇷 [한국어](i18n/ko/ARCHITECTURE.md) | 🇲🇾 [Bahasa Melayu](i18n/ms/ARCHITECTURE.md) | 🇳🇱 [Nederlands](i18n/nl/ARCHITECTURE.md) | 🇳🇴 [Norsk](i18n/no/ARCHITECTURE.md) | 🇵🇹 [Português (Portugal)](i18n/pt/ARCHITECTURE.md) | 🇷🇴 [Română](i18n/ro/ARCHITECTURE.md) | 🇵🇱 [Polski](i18n/pl/ARCHITECTURE.md) | 🇸🇰 [Slovenčina](i18n/sk/ARCHITECTURE.md) | 🇸🇪 [Svenska](i18n/sv/ARCHITECTURE.md) | 🇵🇭 [Filipino](i18n/phi/ARCHITECTURE.md) | 🇨🇿 [Čeština](i18n/cs/ARCHITECTURE.md)
|
||||
|
||||
_Last updated: 2026-03-04_
|
||||
_Last updated: 2026-03-24_
|
||||
|
||||
## Executive Summary
|
||||
|
||||
@@ -65,6 +65,26 @@ Primary runtime model:
|
||||
- Provider SLA/control plane outside local process
|
||||
- External CLI binaries themselves (Claude CLI, Codex CLI, etc.)
|
||||
|
||||
## Dashboard Surface (Current)
|
||||
|
||||
Main pages under `src/app/(dashboard)/dashboard/`:
|
||||
|
||||
- `/dashboard` — quick start + provider overview
|
||||
- `/dashboard/endpoint` — endpoint proxy + MCP + A2A + API endpoint tabs
|
||||
- `/dashboard/providers` — provider connections and credentials
|
||||
- `/dashboard/combos` — combo strategies, templates, model routing rules
|
||||
- `/dashboard/costs` — cost aggregation and pricing visibility
|
||||
- `/dashboard/analytics` — usage analytics and evaluations
|
||||
- `/dashboard/limits` — quota/rate controls
|
||||
- `/dashboard/cli-tools` — CLI onboarding, runtime detection, config generation
|
||||
- `/dashboard/agents` — detected ACP agents + custom agent registration
|
||||
- `/dashboard/media` — image/video/music playground
|
||||
- `/dashboard/search-tools` — search provider testing and history
|
||||
- `/dashboard/health` — uptime, circuit breakers, rate limits
|
||||
- `/dashboard/logs` — request/proxy/audit/console logs
|
||||
- `/dashboard/settings` — system settings tabs (general, routing, combo defaults, etc.)
|
||||
- `/dashboard/api-manager` — API key lifecycle and model permissions
|
||||
|
||||
## High-Level System Context
|
||||
|
||||
```mermaid
|
||||
|
||||
@@ -9,7 +9,7 @@ cost tracking, model switching, and request logging across every tool.
|
||||
## How It Works
|
||||
|
||||
```
|
||||
Claude / Codex / Gemini CLI / OpenCode / Cline / KiloCode / Continue / Kiro CLI
|
||||
Claude / Codex / OpenCode / Cline / KiloCode / Continue / Kiro / Cursor / Copilot
|
||||
│
|
||||
▼ (all point to OmniRoute)
|
||||
http://YOUR_SERVER:20128/v1
|
||||
@@ -27,21 +27,38 @@ Claude / Codex / Gemini CLI / OpenCode / Cline / KiloCode / Continue / Kiro CLI
|
||||
|
||||
---
|
||||
|
||||
## Supported Tools
|
||||
## Supported Tools (Dashboard Source of Truth)
|
||||
|
||||
| Tool | Command | Type | Install Method |
|
||||
| ---------------- | ------------------- | ----------------- | -------------- |
|
||||
| **Claude Code** | `claude` | CLI | npm |
|
||||
| **OpenAI Codex** | `codex` | CLI | npm |
|
||||
| **Gemini CLI** | `gemini` | CLI | npm |
|
||||
| **OpenCode** | `opencode` | CLI | npm |
|
||||
| **Cline** | `cline` | CLI + VS Code ext | npm |
|
||||
| **KiloCode** | `kilocode` / `kilo` | CLI + VS Code ext | npm |
|
||||
| **Continue** | guide-based | VS Code ext | VS Code |
|
||||
| **Kiro CLI** | `kiro-cli` | CLI | curl installer |
|
||||
| **Cursor** | `cursor` | Desktop app | Download |
|
||||
| **Droid** | web-based | Built-in agent | OmniRoute |
|
||||
| **OpenClaw** | web-based | Built-in agent | OmniRoute |
|
||||
The dashboard cards in `/dashboard/cli-tools` are generated from `src/shared/constants/cliTools.ts`.
|
||||
Current list (v3.0.0-rc.16):
|
||||
|
||||
| Tool | ID | Command | Setup Mode | Install Method |
|
||||
| ---------------- | ------------- | ------------ | ---------- | -------------- |
|
||||
| **Claude Code** | `claude` | `claude` | env | npm |
|
||||
| **OpenAI Codex** | `codex` | `codex` | custom | npm |
|
||||
| **Factory Droid**| `droid` | `droid` | custom | bundled/CLI |
|
||||
| **OpenClaw** | `openclaw` | `openclaw` | custom | bundled/CLI |
|
||||
| **Cursor** | `cursor` | app | guide | desktop app |
|
||||
| **Cline** | `cline` | `cline` | custom | npm |
|
||||
| **Kilo Code** | `kilo` | `kilocode` | custom | npm |
|
||||
| **Continue** | `continue` | extension | guide | VS Code |
|
||||
| **Antigravity** | `antigravity` | internal | mitm | OmniRoute |
|
||||
| **GitHub Copilot**| `copilot` | extension | custom | VS Code |
|
||||
| **OpenCode** | `opencode` | `opencode` | guide | npm |
|
||||
| **Kiro AI** | `kiro` | app/cli | mitm | desktop/CLI |
|
||||
|
||||
### CLI fingerprint sync (Agents + Settings)
|
||||
|
||||
`/dashboard/agents` and `Settings > CLI Fingerprint` use `src/shared/constants/cliCompatProviders.ts`.
|
||||
This keeps provider IDs aligned with CLI cards and legacy IDs.
|
||||
|
||||
| CLI ID | Fingerprint Provider ID |
|
||||
| ------ | ----------------------- |
|
||||
| `kilo` | `kilocode` |
|
||||
| `copilot` | `github` |
|
||||
| `claude` / `codex` / `antigravity` / `kiro` / `cursor` / `cline` / `opencode` / `droid` / `openclaw` | same ID |
|
||||
|
||||
Legacy IDs still accepted for compatibility: `copilot`, `kimi-coding`, `qwen`.
|
||||
|
||||
---
|
||||
|
||||
@@ -67,9 +84,6 @@ npm install -g @anthropic-ai/claude-code
|
||||
# OpenAI Codex
|
||||
npm install -g @openai/codex
|
||||
|
||||
# Gemini CLI (Google)
|
||||
npm install -g @google/gemini-cli
|
||||
|
||||
# OpenCode
|
||||
npm install -g opencode-ai
|
||||
|
||||
@@ -77,7 +91,7 @@ npm install -g opencode-ai
|
||||
npm install -g cline
|
||||
|
||||
# KiloCode
|
||||
npm install -g kilecode
|
||||
npm install -g kilocode
|
||||
|
||||
# Kiro CLI (Amazon — requires curl + unzip)
|
||||
apt-get install -y unzip # on Debian/Ubuntu
|
||||
@@ -90,7 +104,6 @@ export PATH="$HOME/.local/bin:$PATH" # add to ~/.bashrc
|
||||
```bash
|
||||
claude --version # 2.x.x
|
||||
codex --version # 0.x.x
|
||||
gemini --version # 0.x.x
|
||||
opencode --version # x.x.x
|
||||
cline --version # 2.x.x
|
||||
kilocode --version # x.x.x (or: kilo --version)
|
||||
@@ -153,21 +166,6 @@ EOF
|
||||
|
||||
---
|
||||
|
||||
### Gemini CLI
|
||||
|
||||
```bash
|
||||
mkdir -p ~/.gemini && cat > ~/.gemini/settings.json << EOF
|
||||
{
|
||||
"apiKey": "sk-your-omniroute-key",
|
||||
"baseUrl": "http://localhost:20128/v1"
|
||||
}
|
||||
EOF
|
||||
```
|
||||
|
||||
**Test:** `gemini "hello"`
|
||||
|
||||
---
|
||||
|
||||
### OpenCode
|
||||
|
||||
```bash
|
||||
@@ -324,17 +322,16 @@ They run as internal routes and use OmniRoute's model routing automatically.
|
||||
OMNIROUTE_URL="http://localhost:20128/v1"
|
||||
OMNIROUTE_KEY="sk-your-omniroute-key"
|
||||
|
||||
npm install -g @anthropic-ai/claude-code @openai/codex @google/gemini-cli opencode-ai cline kilecode
|
||||
npm install -g @anthropic-ai/claude-code @openai/codex opencode-ai cline kilocode
|
||||
|
||||
# Kiro CLI
|
||||
apt-get install -y unzip 2>/dev/null; curl -fsSL https://cli.kiro.dev/install | bash
|
||||
|
||||
# Write configs
|
||||
mkdir -p ~/.claude ~/.codex ~/.gemini ~/.config/opencode ~/.continue
|
||||
mkdir -p ~/.claude ~/.codex ~/.config/opencode ~/.continue
|
||||
|
||||
cat > ~/.claude/settings.json <<< "{\"apiBaseUrl\":\"$OMNIROUTE_URL\",\"apiKey\":\"$OMNIROUTE_KEY\"}"
|
||||
cat > ~/.codex/config.yaml <<< "model: auto\napiKey: $OMNIROUTE_KEY\napiBaseUrl: $OMNIROUTE_URL"
|
||||
cat > ~/.gemini/settings.json <<< "{\"apiKey\":\"$OMNIROUTE_KEY\",\"baseUrl\":\"$OMNIROUTE_URL\"}"
|
||||
cat >> ~/.bashrc << EOF
|
||||
export OPENAI_BASE_URL="$OMNIROUTE_URL"
|
||||
export OPENAI_API_KEY="$OMNIROUTE_KEY"
|
||||
|
||||
@@ -8,73 +8,6 @@ _وكيل API العالمي الخاص بك - نقطة نهاية واحدة،
|
||||
|
||||
---
|
||||
|
||||
### 🆕 الجديد في v2.7.0
|
||||
|
||||
- **RouterStrategy قابل للتوصيل** — استراتيجيات القواعد والتكلفة والكمون
|
||||
- **كشف النية متعدد اللغات** — تسجيل التوجيه بأكثر من 30 لغة
|
||||
- **إلغاء تكرار الطلبات** — تجنب مكالمات API المكررة عبر تجزئة المحتوى
|
||||
- **مزودون جدد:** Grok-4 Fast (xAI) وGLM-5 / Z.AI وMiniMax M2.5 وKimi K2.5
|
||||
- **أسعار محدثة:** Grok-4 Fast $0.20/$0.50/M، GLM-5 $0.50/M، MiniMax M2.5 $0.30/M
|
||||
|
||||
---
|
||||
|
||||
<div align="center">
|
||||
|
||||
[](https://www.npmjs.com/package/omniroute)
|
||||
[](https://hub.docker.com/r/diegosouzapw/omniroute)
|
||||
[](https://github.com/diegosouzapw/OmniRoute/blob/main/LICENSE)
|
||||
[](https://omniroute.online)
|
||||
[](https://chat.whatsapp.com/JI7cDQ1GyaiDHhVBpLxf8b?mode=gi_t)
|
||||
|
||||
[🌐 الموقع الإلكتروني](https://omniroute.online) • [🚀 البداية السريعة](#-quick-start) • [💡 الميزات](#-key-features) • [📖 المستندات](#-documentation) • [💰 التسعير](#-pricing-at-a-glance) • [💬 واتساب](https://chat.whatsapp.com/JI7cDQ1GyaiDHhVBpLxf8b?mode=gi_t)
|
||||
|
||||
</div>
|
||||
|
||||
🌐 **متوفر باللغة:** 🇺🇸 [الإنجليزية](../../README.md) | 🇧🇷 [البرتغالية (البرازيل)](../pt-BR/README.md) | 🇪🇸 [الإسبانية](../es/README.md) | 🇫🇷 [Français](../fr/README.md) | 🇮🇹 [الإيطالية](../it/README.md) | 🇷🇺 [Русский](../ru/README.md) | 🇨🇳 [中文 (简体)](../zh-CN/README.md) | 🇩🇪 [الألمانية](../de/README.md) | 🇮🇳 [هندي](../in/README.md) | 🇹🇭 [ไทย](../th/README.md) | 🇺🇦 [أوكرانيا](../uk-UA/README.md) | 🇸🇦 [العربية](../ar/README.md) | 🇯🇵 [日本語](../ja/README.md) | 🇻🇳 [تيانج فيت](../vi/README.md) | 🇧🇬 [بلغارسكي](../bg/README.md) | 🇩🇰 [الدانسك](../da/README.md) | 🇫🇮 [سومي](../fi/README.md) | 🇮🇱 [العربية](../he/README.md) | 🇭🇺 [المجرية](../hu/README.md) | 🇮🇩 [البهاسا الإندونيسية](../id/README.md) | 🇰🇷 [한국어](../ko/README.md) | 🇲🇾 [البهاسا ملايو](../ms/README.md) | 🇳🇱 [هولندا](../nl/README.md) | 🇳🇴 [نورسك](../no/README.md) | 🇵🇹 [البرتغالية (البرتغال)](../pt/README.md) | 🇷🇴 [روماني](../ro/README.md) | 🇵🇱 [بولسكي](../pl/README.md) | 🇸🇰 [سلوفينسينا](../sk/README.md) | 🇸🇪 [سفينسكا](../sv/README.md) | 🇵🇭 [فلبينية](../phi/README.md)
|
||||
|
||||
---
|
||||
|
||||
## 🖼️ لوحة التحكم الرئيسية
|
||||
|
||||
<div align="center">
|
||||
<img src="./docs/screenshots/MainOmniRoute.png" alt="OmniRoute Dashboard" width="800"/>
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
## 📸 معاينة لوحة التحكم
|
||||
|
||||
<details>
|
||||
<summary><b>انقر لرؤية لقطات شاشة لوحة القيادة</b></summary>
|
||||
|
||||
| صفحة | لقطة شاشة |
|
||||
| --------------------- | -------------------------------------------------- |
|
||||
| ** مقدمو الخدمة ** |  |
|
||||
| **المجموعات** |  |
|
||||
| **تحليلات** |  |
|
||||
| **الصحة** |  |
|
||||
| **مترجم** |  |
|
||||
| **الإعدادات** |  |
|
||||
| **أدوات سطر الأوامر** |  |
|
||||
| **سجلات الاستخدام** |  |
|
||||
| **نقطة النهاية** |  |
|
||||
|
||||
</details>
|
||||
|
||||
---
|
||||
|
||||
### 🚀 New in v2.0.9+ — Playground, CLI Fingerprints & ACP
|
||||
|
||||
| Feature | What It Does |
|
||||
| ------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| 🎮 **Model Playground** | Dashboard page to test any model directly — provider/model/endpoint selectors, Monaco Editor, streaming, abort, timing |
|
||||
| 🔏 **CLI Fingerprint Matching** | Per-provider header/body ordering to match native CLI signatures — toggle per provider in Settings > Security. **Your proxy IP is preserved** |
|
||||
| 🤝 **ACP Support (Agent Client Protocol)** | CLI agent discovery (Codex, Claude, Goose, Gemini CLI, OpenClaw), process spawner, `/api/acp/agents` endpoint |
|
||||
| 🤖 **ACP Agents Dashboard** | Debug > Agents page — grid of 14 agents with install status, version, custom agent form for any CLI tool |
|
||||
| 🔧 **Custom Model `apiFormat` Routing** | Custom models with `apiFormat: "responses"` now correctly route to the Responses API translator |
|
||||
| 🏢 **Codex Workspace Isolation** | Multiple Codex workspaces per email — OAuth correctly separates connections by workspace ID |
|
||||
| 🔄 **Electron Auto-Update** | Desktop app checks for updates + auto-install on restart |
|
||||
|
||||
### 🤖 موفر الذكاء الاصطناعي المجاني لوكلاء البرمجة المفضلين لديك
|
||||
|
||||
_قم بتوصيل أي أداة IDE أو CLI مدعومة بالذكاء الاصطناعي من خلال OmniRoute - بوابة واجهة برمجة التطبيقات المجانية للترميز غير المحدود._
|
||||
@@ -159,6 +92,38 @@ _قم بتوصيل أي أداة IDE أو CLI مدعومة بالذكاء الا
|
||||
|
||||
---
|
||||
|
||||
### 🆕 What's New in v3.0.0
|
||||
|
||||
| Area | Change |
|
||||
| --- | --- |
|
||||
| 🔒 **CodeQL Security** | Fixed 10+ CodeQL alerts: polynomial-redos, insecure-randomness, shell-injection |
|
||||
| ✅ **Route Validation** | All 176 API routes validated with Zod schemas + `validateBody()` |
|
||||
| 🐛 **omniModel Tag Leak** | Internal `<omniModel>` tags no longer leak to clients in SSE streams (#585) |
|
||||
| 🔑 **Registered Keys API** | Auto-provision API keys via `POST /api/v1/registered-keys` with quota enforcement |
|
||||
| 🎨 **Provider Icons** | 130+ provider logos via `@lobehub/icons` (SVG) with PNG fallback |
|
||||
| 🔄 **Model Auto-Sync** | 24h scheduler refreshes model lists for 16 providers |
|
||||
| 🌐 **OpenCode Zen/Go** | Two new providers: free tier + subscription tier |
|
||||
| 🔧 **926 Tests** | Full test suite passes with 0 failures |
|
||||
|
||||
|
||||
|
||||
### 🆕 What's New in v3.0.0
|
||||
|
||||
| Area | Change |
|
||||
| --- | --- |
|
||||
| 🔒 **CodeQL Security** | Fixed 10+ CodeQL alerts: polynomial-redos, insecure-randomness, shell-injection |
|
||||
| ✅ **Route Validation** | All 176 API routes validated with Zod schemas + `validateBody()` |
|
||||
| 🐛 **omniModel Tag Leak** | Internal `<omniModel>` tags no longer leak to clients in SSE streams (#585) |
|
||||
| 🔑 **Registered Keys API** | Auto-provision API keys via `POST /api/v1/registered-keys` with quota enforcement |
|
||||
| 🎨 **Provider Icons** | 130+ provider logos via `@lobehub/icons` (SVG) with PNG fallback |
|
||||
| 🔄 **Model Auto-Sync** | 24h scheduler refreshes model lists for 16 providers |
|
||||
| 🌐 **OpenCode Zen/Go** | Two new providers: free tier + subscription tier |
|
||||
| 🔧 **926 Tests** | Full test suite passes with 0 failures |
|
||||
|
||||
---
|
||||
|
||||
|
||||
|
||||
## 🤔 لماذا OmniRoute؟
|
||||
|
||||
**توقف عن إهدار المال وضرب الحدود:**
|
||||
|
||||
@@ -8,73 +8,6 @@ _Вашият универсален API прокси — една крайна
|
||||
|
||||
---
|
||||
|
||||
### 🆕 What's New in v2.7.0
|
||||
|
||||
- **Pluggable RouterStrategy** — rules, cost, and latency routing strategies
|
||||
- **Multilingual intent detection** — routing scoring in 30+ languages
|
||||
- **Request deduplication** — prevent duplicate API calls via content hash
|
||||
- **New providers:** Grok-4 Fast (xAI), GLM-5 / Z.AI, MiniMax M2.5, Kimi K2.5
|
||||
- **Updated pricing:** Grok-4 Fast $0.20/$0.50/M, GLM-5 $0.50/M, MiniMax M2.5 $0.30/M
|
||||
|
||||
---
|
||||
|
||||
<div align="center">
|
||||
|
||||
[](https://www.npmjs.com/package/omniroute)
|
||||
[](https://hub.docker.com/r/diegosouzapw/omniroute)
|
||||
[](https://github.com/diegosouzapw/OmniRoute/blob/main/LICENSE)
|
||||
[](https://omniroute.online)
|
||||
[](https://chat.whatsapp.com/JI7cDQ1GyaiDHhVBpLxf8b?mode=gi_t)
|
||||
|
||||
[🌐 Уебсайт](https://omniroute.online) • [🚀 Бърз старт](#-quick-start) • [💡 Функции](#-key-features) • [📖 Документи](#-documentation) • [💰 Ценообразуване](#-pricing-at-a-glance) • [💬 WhatsApp](https://chat.whatsapp.com/JI7cDQ1GyaiDHhVBpLxf8b?mode=gi_t)
|
||||
|
||||
</div>
|
||||
|
||||
🌐 **Налично на:** 🇺🇸 [английски](../../README.md) | 🇧🇷 [Португалски (Бразилия)](../pt-BR/README.md) | 🇪🇸 [Испански] (../es/README.md) | 🇫🇷 [Français](../fr/README.md) | 🇮🇹 [италиански] (../it/README.md) | 🇷🇺 [Русский](../ru/README.md) | 🇨🇳 [中文 (简体)](../zh-CN/README.md) | 🇩🇪 [Deutsch](../de/README.md) | 🇮🇳 [हिन्दी] (../in/README.md) | 🇹🇭 [ไทย](../th/README.md) | 🇺🇦 [Українська](../uk-UA/README.md) | 🇸🇦 [العربية](../ar/README.md) | 🇯🇵 [日本語](../ja/README.md) | 🇻🇳 [Tiếng Việt](../vi/README.md) | 🇧🇬 [Български](../bg/README.md) | 🇩🇰 [Dansk](../da/README.md) | 🇫🇮 [Suomi](../fi/README.md) | 🇮🇱 [עברית](../he/README.md) | 🇭🇺 [маджарски] (../hu/README.md) | 🇮🇩 [бахаса Индонезия](../id/README.md) | 🇰🇷 [한국어](../ko/README.md) | 🇲🇾 [Bahasa Melayu](../ms/README.md) | 🇳🇱 [Нидерландия](../nl/README.md) | 🇳🇴 [Norsk](../no/README.md) | 🇵🇹 [Português (Португалия)](../pt/README.md) | 🇷🇴 [Română](../ro/README.md) | 🇵🇱 [Полски](../pl/README.md) | 🇸🇰 [Slovenčina](../sk/README.md) | 🇸🇪 [Svenska](../sv/README.md) | 🇵🇭 [филипински] (../phi/README.md)
|
||||
|
||||
---
|
||||
|
||||
## 🖼️ Главно табло за управление
|
||||
|
||||
<div align="center">
|
||||
<img src="./docs/screenshots/MainOmniRoute.png" alt="OmniRoute Dashboard" width="800"/>
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
## 📸 Визуализация на таблото за управление
|
||||
|
||||
<details>
|
||||
<summary><b>Щракнете, за да видите екранни снимки на таблото </b></summary>
|
||||
|
||||
| Страница | Екранна снимка |
|
||||
| -------------------------- | ----------------------------------------------------- |
|
||||
| **Доставчици** |  |
|
||||
| **Комбота** |  |
|
||||
| **Анализ** |  |
|
||||
| **Здраве** |  |
|
||||
| **Преводач** |  |
|
||||
| **Настройки** |  |
|
||||
| **CLI инструменти** |  |
|
||||
| **Регистри за използване** |  |
|
||||
| **Крайна точка** |  |
|
||||
|
||||
</details>
|
||||
|
||||
---
|
||||
|
||||
### 🚀 New in v2.0.9+ — Playground, CLI Fingerprints & ACP
|
||||
|
||||
| Feature | What It Does |
|
||||
| ------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| 🎮 **Model Playground** | Dashboard page to test any model directly — provider/model/endpoint selectors, Monaco Editor, streaming, abort, timing |
|
||||
| 🔏 **CLI Fingerprint Matching** | Per-provider header/body ordering to match native CLI signatures — toggle per provider in Settings > Security. **Your proxy IP is preserved** |
|
||||
| 🤝 **ACP Support (Agent Client Protocol)** | CLI agent discovery (Codex, Claude, Goose, Gemini CLI, OpenClaw), process spawner, `/api/acp/agents` endpoint |
|
||||
| 🤖 **ACP Agents Dashboard** | Debug > Agents page — grid of 14 agents with install status, version, custom agent form for any CLI tool |
|
||||
| 🔧 **Custom Model `apiFormat` Routing** | Custom models with `apiFormat: "responses"` now correctly route to the Responses API translator |
|
||||
| 🏢 **Codex Workspace Isolation** | Multiple Codex workspaces per email — OAuth correctly separates connections by workspace ID |
|
||||
| 🔄 **Electron Auto-Update** | Desktop app checks for updates + auto-install on restart |
|
||||
|
||||
### 🤖 Безплатен доставчик на AI за вашите любими кодиращи агенти
|
||||
|
||||
_Свържете всеки базиран на AI IDE или CLI инструмент чрез OmniRoute — безплатен API шлюз за неограничено кодиране._
|
||||
@@ -159,6 +92,38 @@ _Свържете всеки базиран на AI IDE или CLI инстру
|
||||
|
||||
---
|
||||
|
||||
### 🆕 What's New in v3.0.0
|
||||
|
||||
| Area | Change |
|
||||
| --- | --- |
|
||||
| 🔒 **CodeQL Security** | Fixed 10+ CodeQL alerts: polynomial-redos, insecure-randomness, shell-injection |
|
||||
| ✅ **Route Validation** | All 176 API routes validated with Zod schemas + `validateBody()` |
|
||||
| 🐛 **omniModel Tag Leak** | Internal `<omniModel>` tags no longer leak to clients in SSE streams (#585) |
|
||||
| 🔑 **Registered Keys API** | Auto-provision API keys via `POST /api/v1/registered-keys` with quota enforcement |
|
||||
| 🎨 **Provider Icons** | 130+ provider logos via `@lobehub/icons` (SVG) with PNG fallback |
|
||||
| 🔄 **Model Auto-Sync** | 24h scheduler refreshes model lists for 16 providers |
|
||||
| 🌐 **OpenCode Zen/Go** | Two new providers: free tier + subscription tier |
|
||||
| 🔧 **926 Tests** | Full test suite passes with 0 failures |
|
||||
|
||||
|
||||
|
||||
### 🆕 What's New in v3.0.0
|
||||
|
||||
| Area | Change |
|
||||
| --- | --- |
|
||||
| 🔒 **CodeQL Security** | Fixed 10+ CodeQL alerts: polynomial-redos, insecure-randomness, shell-injection |
|
||||
| ✅ **Route Validation** | All 176 API routes validated with Zod schemas + `validateBody()` |
|
||||
| 🐛 **omniModel Tag Leak** | Internal `<omniModel>` tags no longer leak to clients in SSE streams (#585) |
|
||||
| 🔑 **Registered Keys API** | Auto-provision API keys via `POST /api/v1/registered-keys` with quota enforcement |
|
||||
| 🎨 **Provider Icons** | 130+ provider logos via `@lobehub/icons` (SVG) with PNG fallback |
|
||||
| 🔄 **Model Auto-Sync** | 24h scheduler refreshes model lists for 16 providers |
|
||||
| 🌐 **OpenCode Zen/Go** | Two new providers: free tier + subscription tier |
|
||||
| 🔧 **926 Tests** | Full test suite passes with 0 failures |
|
||||
|
||||
---
|
||||
|
||||
|
||||
|
||||
## 🤔 Защо OmniRoute?
|
||||
|
||||
**Спрете да пилеете пари и да достигате лимити:**
|
||||
|
||||
@@ -18,6 +18,38 @@ _Váš univerzální API proxy – jeden endpoint, více než 44 poskytovatelů,
|
||||
|
||||
---
|
||||
|
||||
### 🆕 What's New in v3.0.0
|
||||
|
||||
| Area | Change |
|
||||
| --- | --- |
|
||||
| 🔒 **CodeQL Security** | Fixed 10+ CodeQL alerts: polynomial-redos, insecure-randomness, shell-injection |
|
||||
| ✅ **Route Validation** | All 176 API routes validated with Zod schemas + `validateBody()` |
|
||||
| 🐛 **omniModel Tag Leak** | Internal `<omniModel>` tags no longer leak to clients in SSE streams (#585) |
|
||||
| 🔑 **Registered Keys API** | Auto-provision API keys via `POST /api/v1/registered-keys` with quota enforcement |
|
||||
| 🎨 **Provider Icons** | 130+ provider logos via `@lobehub/icons` (SVG) with PNG fallback |
|
||||
| 🔄 **Model Auto-Sync** | 24h scheduler refreshes model lists for 16 providers |
|
||||
| 🌐 **OpenCode Zen/Go** | Two new providers: free tier + subscription tier |
|
||||
| 🔧 **926 Tests** | Full test suite passes with 0 failures |
|
||||
|
||||
|
||||
|
||||
### 🆕 What's New in v3.0.0
|
||||
|
||||
| Area | Change |
|
||||
| --- | --- |
|
||||
| 🔒 **CodeQL Security** | Fixed 10+ CodeQL alerts: polynomial-redos, insecure-randomness, shell-injection |
|
||||
| ✅ **Route Validation** | All 176 API routes validated with Zod schemas + `validateBody()` |
|
||||
| 🐛 **omniModel Tag Leak** | Internal `<omniModel>` tags no longer leak to clients in SSE streams (#585) |
|
||||
| 🔑 **Registered Keys API** | Auto-provision API keys via `POST /api/v1/registered-keys` with quota enforcement |
|
||||
| 🎨 **Provider Icons** | 130+ provider logos via `@lobehub/icons` (SVG) with PNG fallback |
|
||||
| 🔄 **Model Auto-Sync** | 24h scheduler refreshes model lists for 16 providers |
|
||||
| 🌐 **OpenCode Zen/Go** | Two new providers: free tier + subscription tier |
|
||||
| 🔧 **926 Tests** | Full test suite passes with 0 failures |
|
||||
|
||||
---
|
||||
|
||||
|
||||
|
||||
## 🖼️ Hlavní ovládací panel
|
||||
|
||||
<div align="center"> <img src="./docs/screenshots/MainOmniRoute.png" alt="Řídicí panel OmniRoute" width="800"> </div>
|
||||
@@ -165,7 +197,7 @@ OpenAI používá jeden formát, Claude (Anthropic) jiný a Gemini ještě třet
|
||||
|
||||
**Jak to OmniRoute řeší:**
|
||||
|
||||
- **Sjednocený koncový bod** — Jeden `http://localhost:20128/v1` slouží jako proxy pro všech 44+ poskytovatelů.
|
||||
- **Sjednocený koncový bod** — Jeden `http://localhost:20128/v1` slouží jako proxy pro všech 67+ poskytovatelů.
|
||||
- **Překlad formátu** — Automatický a transparentní: OpenAI ↔ Claude ↔ Gemini ↔ Responses API
|
||||
- **Sanitizace odpovědí** — Odstraňuje nestandardní pole ( `x_groq` , `usage_breakdown` , `service_tier` ), která porušují OpenAI SDK v1.83+
|
||||
- **Normalizace rolí** — Převádí `developer` → `system` pro poskytovatele bez OpenAI; `system` → `user` pro GLM/ERNIE
|
||||
@@ -951,31 +983,6 @@ Pak v `/dashboard/media` → záložka **Přepis** : nahrajte libovolný zvukov
|
||||
|
||||
OmniRoute v2.0 je navržen jako operační platforma, nikoli pouze jako proxy pro relé.
|
||||
|
||||
### 🆕 Nové — Vylepšení inspirovaná ClawRouterem (březen 2026)
|
||||
|
||||
| Funkce | Co to dělá |
|
||||
| ---------------------------------------------- | ---------------------------------------------------------------------------------------------------------- |
|
||||
| ⚡ **Grok-4 Rychlá rodina** | Modely xAI za 0,20 USD/0,50 USD/M – v benchmarku 1143 ms (o 30 % rychlejší než Gemini 2.5 Flash) |
|
||||
| 🧠 **GLM-5 přes Z.AI** | 128 tisíc výstupních dat, 0,5 USD/1 milion USD – nejnovější vlajková loď rodiny GLM |
|
||||
| 🔮 **MiniMax M2.5** | Úvaha + agentní úkoly za 0,30 USD/1 milion – významný upgrade oproti M2.1 |
|
||||
| 🎯 **Příznak volání nástroje pro každý model** | `toolCalling: true/false` v registru — AutoCombo přeskakuje modely, které nepodporují nástroje. |
|
||||
| 🌍 **Detekce vícejazyčného záměru** | Klíčová slova PT/ZH/ES/AR v bodování AutoCombo – lepší výběr modelu pro neanglický obsah |
|
||||
| 📊 **Záložní metody řízené benchmarkem** | Skutečná latence p95 z živých požadavků poskytuje kombinované skóre – AutoCombo se učí ze skutečných dat |
|
||||
| 🔁 **Požádat o deduplikaci** | Okno pro deduplikaci na základě hashování obsahu – bezpečné pro více agentů, zabraňuje duplicitním platbám |
|
||||
| 🔌 **Strategie pro zásuvné routery** | Rozšiřitelné rozhraní `RouterStrategy` – přidejte si vlastní logiku směrování jako pluginy |
|
||||
|
||||
### 🚀 Předchozí verze v2.0.9+ — Hřiště, otisky prstů v CLI a ACP
|
||||
|
||||
| Funkce | Co to dělá |
|
||||
| ---------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| 🎮 **Modelové hřiště** | Stránka řídicího panelu pro přímé testování libovolného modelu – selektory poskytovatele/modelu/koncového bodu, editor Monaco, streamování, přerušení, načasování |
|
||||
| 🔏 **Porovnávání otisků prstů v CLI** | Řazení hlaviček/těl serveru podle poskytovatele tak, aby odpovídalo nativním podpisům CLI – přepínání pro jednotlivé poskytovatele v Nastavení > Zabezpečení. **Vaše IP adresa proxy serveru je zachována.** |
|
||||
| 🤝 **Podpora ACP (Agent Client Protocol)** | Vyhledávání agentů CLI (Codex, Claude, Goose, Gemini CLI, OpenClaw + 9 dalších), generátor procesů, koncový bod `/api/acp/agents` |
|
||||
| 🤖 **Řídicí panel agentů ACP** | Ladění › Stránka Agenti — mřížka 14 agentů se stavem instalace, verzí a formulářem pro vlastní agenta pro libovolný nástroj CLI. Uživatelé **OpenCode** získají tlačítko „Stáhnout opencode.json“, které automaticky vygeneruje konfiguraci připravenou k použití se všemi dostupnými modely. |
|
||||
| 🔧 **Směrování `apiFormat` pro vlastní model** | Vlastní modely s `apiFormat: "responses"` nyní správně směrují do překladače Responses API. |
|
||||
| 🏢 **Izolace pracovního prostoru Codexu** | Více pracovních prostorů Codexu na jeden e-mail – OAuth správně odděluje připojení podle ID pracovního prostoru |
|
||||
| 🔄 **Automatická aktualizace elektronů** | Desktopová aplikace kontroluje aktualizace + automaticky se instaluje po restartu |
|
||||
|
||||
### 🤖 Operace s agenty a protokoly (v2.0)
|
||||
|
||||
| Funkce | Co to dělá |
|
||||
|
||||
@@ -8,73 +8,6 @@ _Din universelle API-proxy — ét slutpunkt, 36+ udbydere, ingen nedetid. Nu me
|
||||
|
||||
---
|
||||
|
||||
### 🆕 What's New in v2.7.0
|
||||
|
||||
- **Pluggable RouterStrategy** — rules, cost, and latency routing strategies
|
||||
- **Multilingual intent detection** — routing scoring in 30+ languages
|
||||
- **Request deduplication** — prevent duplicate API calls via content hash
|
||||
- **New providers:** Grok-4 Fast (xAI), GLM-5 / Z.AI, MiniMax M2.5, Kimi K2.5
|
||||
- **Updated pricing:** Grok-4 Fast $0.20/$0.50/M, GLM-5 $0.50/M, MiniMax M2.5 $0.30/M
|
||||
|
||||
---
|
||||
|
||||
<div align="center">
|
||||
|
||||
[](https://www.npmjs.com/package/omniroute)
|
||||
[](https://hub.docker.com/r/diegosouzapw/omniroute)
|
||||
[](https://github.com/diegosouzapw/OmniRoute/blob/main/LICENSE)
|
||||
[](https://omniroute.online)
|
||||
[](https://chat.whatsapp.com/JI7cDQ1GyaiDHhVBpLxf8b?mode=gi_t)
|
||||
|
||||
[🌐 Hjemmeside](https://omniroute.online) • [🚀 Hurtig start](#-quick-start) • [💡 Funktioner](#-key-features) • [📖 Docs](#-documentation) • [💡 Priser](#-pricing-at-a-glance) • [💬 WhatsApp](https://chat.whatsapp.com/JI7cDQ1GyaiDHhVBpLxf8b?mode=gi_t)
|
||||
|
||||
</div>
|
||||
|
||||
🌐 **Tilgængelig på:** 🇺🇸 [engelsk](../../README.md) | 🇧🇷 [Português (Brasil)](../pt-BR/README.md) | 🇪🇸 [Español](../es/README.md) | 🇫🇷 [Français](../fr/README.md) | 🇮🇹 [Italiano](../it/README.md) | 🇷🇺 [Русский](../ru/README.md) | 🇨🇳 [中文 (简体)](../zh-CN/README.md) | 🇩🇪 [Tysk](../de/README.md) | 🇮🇳 [हिन्दी](../in/README.md) | 🇹🇭 [ไทย](../th/README.md) | 🇺🇦 [Українська](../uk-UA/README.md) | 🇸🇦 [العربية](../ar/README.md) | 🇯🇵 [日本語](../ja/README.md) | 🇻🇳 [Tiếng Việt](../vi/README.md) | 🇧🇬 [Български](../bg/README.md) | 🇩🇰 [Dansk](../da/README.md) | 🇫🇮 [Suomi](../fi/README.md) | 🇮🇱 [engelsk](../he/README.md) | 🇭🇺 [Magyar](../hu/README.md) | 🇮🇩 [Bahasa Indonesien](../id/README.md) | 🇰🇷 [한국어](../ko/README.md) | 🇲🇾 [Bahasa Melayu](../ms/README.md) | 🇳🇱 [Nederlands](../nl/README.md) | 🇳🇴 [norsk](../no/README.md) | 🇵🇹 [Português (Portugal)](../pt/README.md) | 🇷🇴 [Română](../ro/README.md) | 🇵🇱 [Polski](../pl/README.md) | 🇸🇰 [Slovenčina](../sk/README.md) | 🇸🇪 [Svenska](../sv/README.md) | 🇵🇭 [filippinsk](../phi/README.md)
|
||||
|
||||
---
|
||||
|
||||
## 🖼️ Hovedbetjeningspanel
|
||||
|
||||
<div align="center">
|
||||
<img src="./docs/screenshots/MainOmniRoute.png" alt="OmniRoute Dashboard" width="800"/>
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
## 📸 Dashboard Preview
|
||||
|
||||
<details>
|
||||
<summary><b>Klik for at se skærmbilleder af dashboard</b></summary>
|
||||
|
||||
| Side | Skærmbillede |
|
||||
| ----------------- | --------------------------------------------------- |
|
||||
| **Udbydere** |  |
|
||||
| **Komboer** |  |
|
||||
| **Analyse** |  |
|
||||
| **Sundhed** |  |
|
||||
| **Oversætter** |  |
|
||||
| **Indstillinger** |  |
|
||||
| **CLI-værktøjer** |  |
|
||||
| **Brugslogfiler** |  |
|
||||
| **Endpunkt** |  |
|
||||
|
||||
</details>
|
||||
|
||||
---
|
||||
|
||||
### 🚀 New in v2.0.9+ — Playground, CLI Fingerprints & ACP
|
||||
|
||||
| Feature | What It Does |
|
||||
| ------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| 🎮 **Model Playground** | Dashboard page to test any model directly — provider/model/endpoint selectors, Monaco Editor, streaming, abort, timing |
|
||||
| 🔏 **CLI Fingerprint Matching** | Per-provider header/body ordering to match native CLI signatures — toggle per provider in Settings > Security. **Your proxy IP is preserved** |
|
||||
| 🤝 **ACP Support (Agent Client Protocol)** | CLI agent discovery (Codex, Claude, Goose, Gemini CLI, OpenClaw), process spawner, `/api/acp/agents` endpoint |
|
||||
| 🤖 **ACP Agents Dashboard** | Debug > Agents page — grid of 14 agents with install status, version, custom agent form for any CLI tool |
|
||||
| 🔧 **Custom Model `apiFormat` Routing** | Custom models with `apiFormat: "responses"` now correctly route to the Responses API translator |
|
||||
| 🏢 **Codex Workspace Isolation** | Multiple Codex workspaces per email — OAuth correctly separates connections by workspace ID |
|
||||
| 🔄 **Electron Auto-Update** | Desktop app checks for updates + auto-install on restart |
|
||||
|
||||
### 🤖 Gratis AI-udbyder til dine foretrukne kodningsagenter
|
||||
|
||||
_Tilslut ethvert AI-drevet IDE- eller CLI-værktøj gennem OmniRoute - gratis API-gateway til ubegrænset kodning._
|
||||
@@ -159,6 +92,38 @@ _Tilslut ethvert AI-drevet IDE- eller CLI-værktøj gennem OmniRoute - gratis AP
|
||||
|
||||
---
|
||||
|
||||
### 🆕 What's New in v3.0.0
|
||||
|
||||
| Area | Change |
|
||||
| --- | --- |
|
||||
| 🔒 **CodeQL Security** | Fixed 10+ CodeQL alerts: polynomial-redos, insecure-randomness, shell-injection |
|
||||
| ✅ **Route Validation** | All 176 API routes validated with Zod schemas + `validateBody()` |
|
||||
| 🐛 **omniModel Tag Leak** | Internal `<omniModel>` tags no longer leak to clients in SSE streams (#585) |
|
||||
| 🔑 **Registered Keys API** | Auto-provision API keys via `POST /api/v1/registered-keys` with quota enforcement |
|
||||
| 🎨 **Provider Icons** | 130+ provider logos via `@lobehub/icons` (SVG) with PNG fallback |
|
||||
| 🔄 **Model Auto-Sync** | 24h scheduler refreshes model lists for 16 providers |
|
||||
| 🌐 **OpenCode Zen/Go** | Two new providers: free tier + subscription tier |
|
||||
| 🔧 **926 Tests** | Full test suite passes with 0 failures |
|
||||
|
||||
|
||||
|
||||
### 🆕 What's New in v3.0.0
|
||||
|
||||
| Area | Change |
|
||||
| --- | --- |
|
||||
| 🔒 **CodeQL Security** | Fixed 10+ CodeQL alerts: polynomial-redos, insecure-randomness, shell-injection |
|
||||
| ✅ **Route Validation** | All 176 API routes validated with Zod schemas + `validateBody()` |
|
||||
| 🐛 **omniModel Tag Leak** | Internal `<omniModel>` tags no longer leak to clients in SSE streams (#585) |
|
||||
| 🔑 **Registered Keys API** | Auto-provision API keys via `POST /api/v1/registered-keys` with quota enforcement |
|
||||
| 🎨 **Provider Icons** | 130+ provider logos via `@lobehub/icons` (SVG) with PNG fallback |
|
||||
| 🔄 **Model Auto-Sync** | 24h scheduler refreshes model lists for 16 providers |
|
||||
| 🌐 **OpenCode Zen/Go** | Two new providers: free tier + subscription tier |
|
||||
| 🔧 **926 Tests** | Full test suite passes with 0 failures |
|
||||
|
||||
---
|
||||
|
||||
|
||||
|
||||
## 🤔 Hvorfor OmniRoute?
|
||||
|
||||
**Stop med at spilde penge og nå grænser:**
|
||||
|
||||
@@ -8,61 +8,6 @@ _Ihr universeller API-Proxy – ein Endpunkt, mehr als 36 Anbieter, keine Ausfal
|
||||
|
||||
---
|
||||
|
||||
### 🆕 Neu in v2.7.0
|
||||
|
||||
- **Erweiterbare RouterStrategy** — Regeln-, Kosten- und Latenzstrategien
|
||||
- **Mehrsprachige Absichtserkennung** — Routing-Scoring in 30+ Sprachen
|
||||
- **Anfrage-Deduplizierung** — doppelte API-Aufrufe per Content-Hash vermeiden
|
||||
- **Neue Anbieter:** Grok-4 Fast (xAI), GLM-5 / Z.AI, MiniMax M2.5, Kimi K2.5
|
||||
- **Aktualisierte Preise:** Grok-4 Fast $0.20/$0.50/M, GLM-5 $0.50/M, MiniMax M2.5 $0.30/M
|
||||
|
||||
---
|
||||
|
||||
<div align="center">
|
||||
|
||||
[](https://www.npmjs.com/package/omniroute)
|
||||
[](https://hub.docker.com/r/diegosouzapw/omniroute)
|
||||
[](https://github.com/diegosouzapw/OmniRoute/blob/main/LICENSE)
|
||||
[](https://omniroute.online)
|
||||
[](https://chat.whatsapp.com/JI7cDQ1GyaiDHhVBpLxf8b?mode=gi_t)
|
||||
|
||||
[🌐 Website](https://omniroute.online) • [🚀 Schnellstart](#-quick-start) • [💡 Funktionen](#-key-features) • [📖 Dokumente](#-documentation) • [💰 Preise](#-pricing-at-a-glance) • [💬 WhatsApp](https://chat.whatsapp.com/JI7cDQ1GyaiDHhVBpLxf8b?mode=gi_t)
|
||||
|
||||
</div>
|
||||
|
||||
🌐 **Verfügbar in:** 🇺🇸 [Englisch](../../README.md) | 🇧🇷 [Português (Brasilien)](../pt-BR/README.md) | 🇪🇸 [Español](../es/README.md) | 🇫🇷 [Français](../fr/README.md) | 🇮🇹 [Italienisch](../it/README.md) | 🇷🇺 [Русский](../ru/README.md) | 🇨🇳 [中文 (简体)](../zh-CN/README.md) | 🇩🇪 [Deutsch](../de/README.md) | 🇮🇳 [हिन्दी](../in/README.md) | 🇹🇭 [ไทย](../th/README.md) | 🇺🇦 [Українська](../uk-UA/README.md) | 🇸🇦 [العربية](../ar/README.md) | 🇯🇵 [日本語](../ja/README.md) | 🇻🇳 [Tiếng Việt](../vi/README.md) | 🇧🇬 [Български](../bg/README.md) | 🇩🇰 [Dänisch](../da/README.md) | 🇫🇮 [Suomi](../fi/README.md) | 🇮🇱 [עברית](../he/README.md) | 🇭🇺 [Magyar](../hu/README.md) | 🇮🇩 [Bahasa Indonesia](../id/README.md) | 🇰🇷 [한국어](../ko/README.md) | 🇲🇾 [Bahasa Melayu](../ms/README.md) | 🇳🇱 [Niederlande](../nl/README.md) | 🇳🇴 [Norsk](../no/README.md) | 🇵🇹 [Português (Portugal)](../pt/README.md) | 🇷🇴 [Română](../ro/README.md) | 🇵🇱 [Polski](../pl/README.md) | 🇸🇰 [Slovenčina](../sk/README.md) | 🇸🇪 [Svenska](../sv/README.md) | 🇵🇭 [Philippinisch](../phi/README.md)
|
||||
|
||||
---
|
||||
|
||||
## 🖼️ Haupt-Dashboard
|
||||
|
||||
<div align="center">
|
||||
<img src="./docs/screenshots/MainOmniRoute.png" alt="OmniRoute Dashboard" width="800"/>
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
## 📸 Dashboard-Vorschau
|
||||
|
||||
<details>
|
||||
<summary><b>Klicken Sie hier, um Dashboard-Screenshots anzuzeigen</b></summary>
|
||||
|
||||
| Seite | Screenshot |
|
||||
| ---------------------- | -------------------------------------------------- |
|
||||
| **Anbieter** |  |
|
||||
| **Kombinationen** |  |
|
||||
| **Analytik** |  |
|
||||
| **Gesundheit** |  |
|
||||
| **Übersetzer** |  |
|
||||
| **Einstellungen** |  |
|
||||
| **CLI-Tools** |  |
|
||||
| **Nutzungsprotokolle** |  |
|
||||
| **Endpunkt** |  |
|
||||
|
||||
</details>
|
||||
|
||||
---
|
||||
|
||||
### 🤖 Kostenloser KI-Anbieter für Ihre bevorzugten Programmieragenten
|
||||
|
||||
_Verbinden Sie jedes KI-gestützte IDE- oder CLI-Tool über OmniRoute – kostenloses API-Gateway für unbegrenzte Codierung._
|
||||
@@ -147,6 +92,38 @@ _Verbinden Sie jedes KI-gestützte IDE- oder CLI-Tool über OmniRoute – kosten
|
||||
|
||||
---
|
||||
|
||||
### 🆕 What's New in v3.0.0
|
||||
|
||||
| Area | Change |
|
||||
| --- | --- |
|
||||
| 🔒 **CodeQL Security** | Fixed 10+ CodeQL alerts: polynomial-redos, insecure-randomness, shell-injection |
|
||||
| ✅ **Route Validation** | All 176 API routes validated with Zod schemas + `validateBody()` |
|
||||
| 🐛 **omniModel Tag Leak** | Internal `<omniModel>` tags no longer leak to clients in SSE streams (#585) |
|
||||
| 🔑 **Registered Keys API** | Auto-provision API keys via `POST /api/v1/registered-keys` with quota enforcement |
|
||||
| 🎨 **Provider Icons** | 130+ provider logos via `@lobehub/icons` (SVG) with PNG fallback |
|
||||
| 🔄 **Model Auto-Sync** | 24h scheduler refreshes model lists for 16 providers |
|
||||
| 🌐 **OpenCode Zen/Go** | Two new providers: free tier + subscription tier |
|
||||
| 🔧 **926 Tests** | Full test suite passes with 0 failures |
|
||||
|
||||
|
||||
|
||||
### 🆕 What's New in v3.0.0
|
||||
|
||||
| Area | Change |
|
||||
| --- | --- |
|
||||
| 🔒 **CodeQL Security** | Fixed 10+ CodeQL alerts: polynomial-redos, insecure-randomness, shell-injection |
|
||||
| ✅ **Route Validation** | All 176 API routes validated with Zod schemas + `validateBody()` |
|
||||
| 🐛 **omniModel Tag Leak** | Internal `<omniModel>` tags no longer leak to clients in SSE streams (#585) |
|
||||
| 🔑 **Registered Keys API** | Auto-provision API keys via `POST /api/v1/registered-keys` with quota enforcement |
|
||||
| 🎨 **Provider Icons** | 130+ provider logos via `@lobehub/icons` (SVG) with PNG fallback |
|
||||
| 🔄 **Model Auto-Sync** | 24h scheduler refreshes model lists for 16 providers |
|
||||
| 🌐 **OpenCode Zen/Go** | Two new providers: free tier + subscription tier |
|
||||
| 🔧 **926 Tests** | Full test suite passes with 0 failures |
|
||||
|
||||
---
|
||||
|
||||
|
||||
|
||||
## 🤔 Warum OmniRoute?
|
||||
|
||||
**Hören Sie auf, Geld zu verschwenden und an Grenzen zu stoßen:**
|
||||
@@ -890,18 +867,6 @@ Wenn OmniRoute minimiert ist, befindet es sich mit schnellen Aktionen in Ihrer T
|
||||
|
||||
OmniRoute v2.0 ist als Betriebsplattform konzipiert und nicht nur als Relay-Proxy.
|
||||
|
||||
### 🚀 New in v2.0.9+ — Playground, CLI Fingerprints & ACP
|
||||
|
||||
| Feature | What It Does |
|
||||
| ------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| 🎮 **Model Playground** | Dashboard page to test any model directly — provider/model/endpoint selectors, Monaco Editor, streaming, abort, timing |
|
||||
| 🔏 **CLI Fingerprint Matching** | Per-provider header/body ordering to match native CLI signatures — toggle per provider in Settings > Security. **Your proxy IP is preserved** |
|
||||
| 🤝 **ACP Support (Agent Client Protocol)** | CLI agent discovery (Codex, Claude, Goose, Gemini CLI, OpenClaw), process spawner, `/api/acp/agents` endpoint |
|
||||
| 🤖 **ACP Agents Dashboard** | Debug > Agents page — grid of 14 agents with install status, version, custom agent form for any CLI tool |
|
||||
| 🔧 **Custom Model `apiFormat` Routing** | Custom models with `apiFormat: "responses"` now correctly route to the Responses API translator |
|
||||
| 🏢 **Codex Workspace Isolation** | Multiple Codex workspaces per email — OAuth correctly separates connections by workspace ID |
|
||||
| 🔄 **Electron Auto-Update** | Desktop app checks for updates + auto-install on restart |
|
||||
|
||||
### 🤖 Agenten- und Protokolloperationen (v2.0)| Funktion | Was es tut |
|
||||
|
||||
| ------------------------------------ | -------------------------------------------------------------------------------- |
|
||||
|
||||
@@ -11,28 +11,6 @@ _Tu proxy de API universal — un endpoint, 36+ proveedores, cero tiempo de inac
|
||||
|
||||
---
|
||||
|
||||
### 🆕 Novedades en v2.7.0
|
||||
|
||||
- **RouterStrategy enchufable** — estrategias de reglas, costo y latencia
|
||||
- **Detección de intención multilingüe** — puntuación de enrutamiento en 30+ idiomas
|
||||
- **Deduplicación de solicitudes** — evita llamadas duplicadas por hash de contenido
|
||||
- **Nuevos proveedores:** Grok-4 Fast (xAI), GLM-5 / Z.AI, MiniMax M2.5, Kimi K2.5
|
||||
- **Precios actualizados:** Grok-4 Fast $0.20/$0.50/M, GLM-5 $0.50/M, MiniMax M2.5 $0.30/M
|
||||
|
||||
---
|
||||
|
||||
### 🚀 New in v2.0.9+ — Playground, CLI Fingerprints & ACP
|
||||
|
||||
| Feature | What It Does |
|
||||
| ------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| 🎮 **Model Playground** | Dashboard page to test any model directly — provider/model/endpoint selectors, Monaco Editor, streaming, abort, timing |
|
||||
| 🔏 **CLI Fingerprint Matching** | Per-provider header/body ordering to match native CLI signatures — toggle per provider in Settings > Security. **Your proxy IP is preserved** |
|
||||
| 🤝 **ACP Support (Agent Client Protocol)** | CLI agent discovery (Codex, Claude, Goose, Gemini CLI, OpenClaw), process spawner, `/api/acp/agents` endpoint |
|
||||
| 🤖 **ACP Agents Dashboard** | Debug > Agents page — grid of 14 agents with install status, version, custom agent form for any CLI tool |
|
||||
| 🔧 **Custom Model `apiFormat` Routing** | Custom models with `apiFormat: "responses"` now correctly route to the Responses API translator |
|
||||
| 🏢 **Codex Workspace Isolation** | Multiple Codex workspaces per email — OAuth correctly separates connections by workspace ID |
|
||||
| 🔄 **Electron Auto-Update** | Desktop app checks for updates + auto-install on restart |
|
||||
|
||||
### 🤖 Proveedor de IA Gratuito para tus agentes de programación favoritos
|
||||
|
||||
_Conecta cualquier IDE o herramienta CLI con IA a través de OmniRoute — gateway de API gratuito para programación ilimitada._
|
||||
@@ -118,6 +96,38 @@ _Conecta cualquier IDE o herramienta CLI con IA a través de OmniRoute — gatew
|
||||
|
||||
---
|
||||
|
||||
### 🆕 What's New in v3.0.0
|
||||
|
||||
| Area | Change |
|
||||
| --- | --- |
|
||||
| 🔒 **CodeQL Security** | Fixed 10+ CodeQL alerts: polynomial-redos, insecure-randomness, shell-injection |
|
||||
| ✅ **Route Validation** | All 176 API routes validated with Zod schemas + `validateBody()` |
|
||||
| 🐛 **omniModel Tag Leak** | Internal `<omniModel>` tags no longer leak to clients in SSE streams (#585) |
|
||||
| 🔑 **Registered Keys API** | Auto-provision API keys via `POST /api/v1/registered-keys` with quota enforcement |
|
||||
| 🎨 **Provider Icons** | 130+ provider logos via `@lobehub/icons` (SVG) with PNG fallback |
|
||||
| 🔄 **Model Auto-Sync** | 24h scheduler refreshes model lists for 16 providers |
|
||||
| 🌐 **OpenCode Zen/Go** | Two new providers: free tier + subscription tier |
|
||||
| 🔧 **926 Tests** | Full test suite passes with 0 failures |
|
||||
|
||||
|
||||
|
||||
### 🆕 What's New in v3.0.0
|
||||
|
||||
| Area | Change |
|
||||
| --- | --- |
|
||||
| 🔒 **CodeQL Security** | Fixed 10+ CodeQL alerts: polynomial-redos, insecure-randomness, shell-injection |
|
||||
| ✅ **Route Validation** | All 176 API routes validated with Zod schemas + `validateBody()` |
|
||||
| 🐛 **omniModel Tag Leak** | Internal `<omniModel>` tags no longer leak to clients in SSE streams (#585) |
|
||||
| 🔑 **Registered Keys API** | Auto-provision API keys via `POST /api/v1/registered-keys` with quota enforcement |
|
||||
| 🎨 **Provider Icons** | 130+ provider logos via `@lobehub/icons` (SVG) with PNG fallback |
|
||||
| 🔄 **Model Auto-Sync** | 24h scheduler refreshes model lists for 16 providers |
|
||||
| 🌐 **OpenCode Zen/Go** | Two new providers: free tier + subscription tier |
|
||||
| 🔧 **926 Tests** | Full test suite passes with 0 failures |
|
||||
|
||||
---
|
||||
|
||||
|
||||
|
||||
[](https://www.npmjs.com/package/omniroute)
|
||||
[](https://hub.docker.com/r/diegosouzapw/omniroute)
|
||||
[](https://github.com/diegosouzapw/OmniRoute/blob/main/LICENSE)
|
||||
|
||||
@@ -11,28 +11,6 @@ _Universaali API-välityspalvelin – yksi päätepiste, yli 36 palveluntarjoaja
|
||||
|
||||
---
|
||||
|
||||
### 🆕 What's New in v2.7.0
|
||||
|
||||
- **Pluggable RouterStrategy** — rules, cost, and latency routing strategies
|
||||
- **Multilingual intent detection** — routing scoring in 30+ languages
|
||||
- **Request deduplication** — prevent duplicate API calls via content hash
|
||||
- **New providers:** Grok-4 Fast (xAI), GLM-5 / Z.AI, MiniMax M2.5, Kimi K2.5
|
||||
- **Updated pricing:** Grok-4 Fast $0.20/$0.50/M, GLM-5 $0.50/M, MiniMax M2.5 $0.30/M
|
||||
|
||||
---
|
||||
|
||||
### 🚀 New in v2.0.9+ — Playground, CLI Fingerprints & ACP
|
||||
|
||||
| Feature | What It Does |
|
||||
| ------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| 🎮 **Model Playground** | Dashboard page to test any model directly — provider/model/endpoint selectors, Monaco Editor, streaming, abort, timing |
|
||||
| 🔏 **CLI Fingerprint Matching** | Per-provider header/body ordering to match native CLI signatures — toggle per provider in Settings > Security. **Your proxy IP is preserved** |
|
||||
| 🤝 **ACP Support (Agent Client Protocol)** | CLI agent discovery (Codex, Claude, Goose, Gemini CLI, OpenClaw), process spawner, `/api/acp/agents` endpoint |
|
||||
| 🤖 **ACP Agents Dashboard** | Debug > Agents page — grid of 14 agents with install status, version, custom agent form for any CLI tool |
|
||||
| 🔧 **Custom Model `apiFormat` Routing** | Custom models with `apiFormat: "responses"` now correctly route to the Responses API translator |
|
||||
| 🏢 **Codex Workspace Isolation** | Multiple Codex workspaces per email — OAuth correctly separates connections by workspace ID |
|
||||
| 🔄 **Electron Auto-Update** | Desktop app checks for updates + auto-install on restart |
|
||||
|
||||
### 🤖 Ilmainen AI Provider suosikkikoodaajillesi
|
||||
|
||||
_Yhdistä mikä tahansa tekoälyllä toimiva IDE- tai CLI-työkalu OmniRouten kautta – ilmainen API-yhdyskäytävä rajoittamattomaan koodaukseen._
|
||||
@@ -118,6 +96,38 @@ _Yhdistä mikä tahansa tekoälyllä toimiva IDE- tai CLI-työkalu OmniRouten ka
|
||||
|
||||
---
|
||||
|
||||
### 🆕 What's New in v3.0.0
|
||||
|
||||
| Area | Change |
|
||||
| --- | --- |
|
||||
| 🔒 **CodeQL Security** | Fixed 10+ CodeQL alerts: polynomial-redos, insecure-randomness, shell-injection |
|
||||
| ✅ **Route Validation** | All 176 API routes validated with Zod schemas + `validateBody()` |
|
||||
| 🐛 **omniModel Tag Leak** | Internal `<omniModel>` tags no longer leak to clients in SSE streams (#585) |
|
||||
| 🔑 **Registered Keys API** | Auto-provision API keys via `POST /api/v1/registered-keys` with quota enforcement |
|
||||
| 🎨 **Provider Icons** | 130+ provider logos via `@lobehub/icons` (SVG) with PNG fallback |
|
||||
| 🔄 **Model Auto-Sync** | 24h scheduler refreshes model lists for 16 providers |
|
||||
| 🌐 **OpenCode Zen/Go** | Two new providers: free tier + subscription tier |
|
||||
| 🔧 **926 Tests** | Full test suite passes with 0 failures |
|
||||
|
||||
|
||||
|
||||
### 🆕 What's New in v3.0.0
|
||||
|
||||
| Area | Change |
|
||||
| --- | --- |
|
||||
| 🔒 **CodeQL Security** | Fixed 10+ CodeQL alerts: polynomial-redos, insecure-randomness, shell-injection |
|
||||
| ✅ **Route Validation** | All 176 API routes validated with Zod schemas + `validateBody()` |
|
||||
| 🐛 **omniModel Tag Leak** | Internal `<omniModel>` tags no longer leak to clients in SSE streams (#585) |
|
||||
| 🔑 **Registered Keys API** | Auto-provision API keys via `POST /api/v1/registered-keys` with quota enforcement |
|
||||
| 🎨 **Provider Icons** | 130+ provider logos via `@lobehub/icons` (SVG) with PNG fallback |
|
||||
| 🔄 **Model Auto-Sync** | 24h scheduler refreshes model lists for 16 providers |
|
||||
| 🌐 **OpenCode Zen/Go** | Two new providers: free tier + subscription tier |
|
||||
| 🔧 **926 Tests** | Full test suite passes with 0 failures |
|
||||
|
||||
---
|
||||
|
||||
|
||||
|
||||
[](https://www.npmjs.com/package/omniroute)
|
||||
[](https://hub.docker.com/r/diegosouzapw/omniroute)
|
||||
[](https://github.com/diegosouzapw/OmniRoute/blob/main/LICENSE)
|
||||
|
||||
@@ -11,28 +11,6 @@ _Votre proxy API universel — un endpoint, 36+ fournisseurs, zéro temps d'arr
|
||||
|
||||
---
|
||||
|
||||
### 🆕 Nouveautés dans v2.7.0
|
||||
|
||||
- **RouterStrategy extensible** — stratégies de règles, coût et latence
|
||||
- **Détection d'intention multilingue** — scoring de routage en 30+ langues
|
||||
- **Déduplication des requêtes** — évite les appels dupliqués via hash de contenu
|
||||
- **Nouveaux fournisseurs :** Grok-4 Fast (xAI), GLM-5 / Z.AI, MiniMax M2.5, Kimi K2.5
|
||||
- **Tarifs mis à jour :** Grok-4 Fast $0.20/$0.50/M, GLM-5 $0.50/M, MiniMax M2.5 $0.30/M
|
||||
|
||||
---
|
||||
|
||||
### 🚀 New in v2.0.9+ — Playground, CLI Fingerprints & ACP
|
||||
|
||||
| Feature | What It Does |
|
||||
| ------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| 🎮 **Model Playground** | Dashboard page to test any model directly — provider/model/endpoint selectors, Monaco Editor, streaming, abort, timing |
|
||||
| 🔏 **CLI Fingerprint Matching** | Per-provider header/body ordering to match native CLI signatures — toggle per provider in Settings > Security. **Your proxy IP is preserved** |
|
||||
| 🤝 **ACP Support (Agent Client Protocol)** | CLI agent discovery (Codex, Claude, Goose, Gemini CLI, OpenClaw), process spawner, `/api/acp/agents` endpoint |
|
||||
| 🤖 **ACP Agents Dashboard** | Debug > Agents page — grid of 14 agents with install status, version, custom agent form for any CLI tool |
|
||||
| 🔧 **Custom Model `apiFormat` Routing** | Custom models with `apiFormat: "responses"` now correctly route to the Responses API translator |
|
||||
| 🏢 **Codex Workspace Isolation** | Multiple Codex workspaces per email — OAuth correctly separates connections by workspace ID |
|
||||
| 🔄 **Electron Auto-Update** | Desktop app checks for updates + auto-install on restart |
|
||||
|
||||
### 🤖 Fournisseur IA gratuit pour vos agents de programmation préférés
|
||||
|
||||
_Connectez n'importe quel IDE ou outil CLI alimenté par l'IA via OmniRoute — passerelle API gratuite pour un codage illimité._
|
||||
@@ -118,6 +96,38 @@ _Connectez n'importe quel IDE ou outil CLI alimenté par l'IA via OmniRoute —
|
||||
|
||||
---
|
||||
|
||||
### 🆕 What's New in v3.0.0
|
||||
|
||||
| Area | Change |
|
||||
| --- | --- |
|
||||
| 🔒 **CodeQL Security** | Fixed 10+ CodeQL alerts: polynomial-redos, insecure-randomness, shell-injection |
|
||||
| ✅ **Route Validation** | All 176 API routes validated with Zod schemas + `validateBody()` |
|
||||
| 🐛 **omniModel Tag Leak** | Internal `<omniModel>` tags no longer leak to clients in SSE streams (#585) |
|
||||
| 🔑 **Registered Keys API** | Auto-provision API keys via `POST /api/v1/registered-keys` with quota enforcement |
|
||||
| 🎨 **Provider Icons** | 130+ provider logos via `@lobehub/icons` (SVG) with PNG fallback |
|
||||
| 🔄 **Model Auto-Sync** | 24h scheduler refreshes model lists for 16 providers |
|
||||
| 🌐 **OpenCode Zen/Go** | Two new providers: free tier + subscription tier |
|
||||
| 🔧 **926 Tests** | Full test suite passes with 0 failures |
|
||||
|
||||
|
||||
|
||||
### 🆕 What's New in v3.0.0
|
||||
|
||||
| Area | Change |
|
||||
| --- | --- |
|
||||
| 🔒 **CodeQL Security** | Fixed 10+ CodeQL alerts: polynomial-redos, insecure-randomness, shell-injection |
|
||||
| ✅ **Route Validation** | All 176 API routes validated with Zod schemas + `validateBody()` |
|
||||
| 🐛 **omniModel Tag Leak** | Internal `<omniModel>` tags no longer leak to clients in SSE streams (#585) |
|
||||
| 🔑 **Registered Keys API** | Auto-provision API keys via `POST /api/v1/registered-keys` with quota enforcement |
|
||||
| 🎨 **Provider Icons** | 130+ provider logos via `@lobehub/icons` (SVG) with PNG fallback |
|
||||
| 🔄 **Model Auto-Sync** | 24h scheduler refreshes model lists for 16 providers |
|
||||
| 🌐 **OpenCode Zen/Go** | Two new providers: free tier + subscription tier |
|
||||
| 🔧 **926 Tests** | Full test suite passes with 0 failures |
|
||||
|
||||
---
|
||||
|
||||
|
||||
|
||||
[](https://www.npmjs.com/package/omniroute)
|
||||
[](https://hub.docker.com/r/diegosouzapw/omniroute)
|
||||
[](https://github.com/diegosouzapw/OmniRoute/blob/main/LICENSE)
|
||||
|
||||
@@ -11,28 +11,6 @@ _שרת ה-API האוניברסלי שלך - נקודת קצה אחת, 36+ ספ
|
||||
|
||||
---
|
||||
|
||||
### 🆕 What's New in v2.7.0
|
||||
|
||||
- **Pluggable RouterStrategy** — rules, cost, and latency routing strategies
|
||||
- **Multilingual intent detection** — routing scoring in 30+ languages
|
||||
- **Request deduplication** — prevent duplicate API calls via content hash
|
||||
- **New providers:** Grok-4 Fast (xAI), GLM-5 / Z.AI, MiniMax M2.5, Kimi K2.5
|
||||
- **Updated pricing:** Grok-4 Fast $0.20/$0.50/M, GLM-5 $0.50/M, MiniMax M2.5 $0.30/M
|
||||
|
||||
---
|
||||
|
||||
### 🚀 New in v2.0.9+ — Playground, CLI Fingerprints & ACP
|
||||
|
||||
| Feature | What It Does |
|
||||
| ------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| 🎮 **Model Playground** | Dashboard page to test any model directly — provider/model/endpoint selectors, Monaco Editor, streaming, abort, timing |
|
||||
| 🔏 **CLI Fingerprint Matching** | Per-provider header/body ordering to match native CLI signatures — toggle per provider in Settings > Security. **Your proxy IP is preserved** |
|
||||
| 🤝 **ACP Support (Agent Client Protocol)** | CLI agent discovery (Codex, Claude, Goose, Gemini CLI, OpenClaw), process spawner, `/api/acp/agents` endpoint |
|
||||
| 🤖 **ACP Agents Dashboard** | Debug > Agents page — grid of 14 agents with install status, version, custom agent form for any CLI tool |
|
||||
| 🔧 **Custom Model `apiFormat` Routing** | Custom models with `apiFormat: "responses"` now correctly route to the Responses API translator |
|
||||
| 🏢 **Codex Workspace Isolation** | Multiple Codex workspaces per email — OAuth correctly separates connections by workspace ID |
|
||||
| 🔄 **Electron Auto-Update** | Desktop app checks for updates + auto-install on restart |
|
||||
|
||||
### 🤖 ספק AI בחינם עבור סוכני הקידוד המועדפים עליך
|
||||
|
||||
_חבר כל כלי IDE או CLI המופעל על ידי AI דרך OmniRoute - שער API בחינם לקידוד בלתי מוגבל._
|
||||
@@ -118,6 +96,38 @@ _חבר כל כלי IDE או CLI המופעל על ידי AI דרך OmniRoute -
|
||||
|
||||
---
|
||||
|
||||
### 🆕 What's New in v3.0.0
|
||||
|
||||
| Area | Change |
|
||||
| --- | --- |
|
||||
| 🔒 **CodeQL Security** | Fixed 10+ CodeQL alerts: polynomial-redos, insecure-randomness, shell-injection |
|
||||
| ✅ **Route Validation** | All 176 API routes validated with Zod schemas + `validateBody()` |
|
||||
| 🐛 **omniModel Tag Leak** | Internal `<omniModel>` tags no longer leak to clients in SSE streams (#585) |
|
||||
| 🔑 **Registered Keys API** | Auto-provision API keys via `POST /api/v1/registered-keys` with quota enforcement |
|
||||
| 🎨 **Provider Icons** | 130+ provider logos via `@lobehub/icons` (SVG) with PNG fallback |
|
||||
| 🔄 **Model Auto-Sync** | 24h scheduler refreshes model lists for 16 providers |
|
||||
| 🌐 **OpenCode Zen/Go** | Two new providers: free tier + subscription tier |
|
||||
| 🔧 **926 Tests** | Full test suite passes with 0 failures |
|
||||
|
||||
|
||||
|
||||
### 🆕 What's New in v3.0.0
|
||||
|
||||
| Area | Change |
|
||||
| --- | --- |
|
||||
| 🔒 **CodeQL Security** | Fixed 10+ CodeQL alerts: polynomial-redos, insecure-randomness, shell-injection |
|
||||
| ✅ **Route Validation** | All 176 API routes validated with Zod schemas + `validateBody()` |
|
||||
| 🐛 **omniModel Tag Leak** | Internal `<omniModel>` tags no longer leak to clients in SSE streams (#585) |
|
||||
| 🔑 **Registered Keys API** | Auto-provision API keys via `POST /api/v1/registered-keys` with quota enforcement |
|
||||
| 🎨 **Provider Icons** | 130+ provider logos via `@lobehub/icons` (SVG) with PNG fallback |
|
||||
| 🔄 **Model Auto-Sync** | 24h scheduler refreshes model lists for 16 providers |
|
||||
| 🌐 **OpenCode Zen/Go** | Two new providers: free tier + subscription tier |
|
||||
| 🔧 **926 Tests** | Full test suite passes with 0 failures |
|
||||
|
||||
---
|
||||
|
||||
|
||||
|
||||
[](https://www.npmjs.com/package/omniroute)
|
||||
[](https://hub.docker.com/r/diegosouzapw/omniroute)
|
||||
[](https://github.com/diegosouzapw/OmniRoute/blob/main/LICENSE)
|
||||
|
||||
@@ -11,28 +11,6 @@ _Az univerzális API-proxy – egy végpont, 36+ szolgáltató, nulla állásid
|
||||
|
||||
---
|
||||
|
||||
### 🆕 What's New in v2.7.0
|
||||
|
||||
- **Pluggable RouterStrategy** — rules, cost, and latency routing strategies
|
||||
- **Multilingual intent detection** — routing scoring in 30+ languages
|
||||
- **Request deduplication** — prevent duplicate API calls via content hash
|
||||
- **New providers:** Grok-4 Fast (xAI), GLM-5 / Z.AI, MiniMax M2.5, Kimi K2.5
|
||||
- **Updated pricing:** Grok-4 Fast $0.20/$0.50/M, GLM-5 $0.50/M, MiniMax M2.5 $0.30/M
|
||||
|
||||
---
|
||||
|
||||
### 🚀 New in v2.0.9+ — Playground, CLI Fingerprints & ACP
|
||||
|
||||
| Feature | What It Does |
|
||||
| ------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| 🎮 **Model Playground** | Dashboard page to test any model directly — provider/model/endpoint selectors, Monaco Editor, streaming, abort, timing |
|
||||
| 🔏 **CLI Fingerprint Matching** | Per-provider header/body ordering to match native CLI signatures — toggle per provider in Settings > Security. **Your proxy IP is preserved** |
|
||||
| 🤝 **ACP Support (Agent Client Protocol)** | CLI agent discovery (Codex, Claude, Goose, Gemini CLI, OpenClaw), process spawner, `/api/acp/agents` endpoint |
|
||||
| 🤖 **ACP Agents Dashboard** | Debug > Agents page — grid of 14 agents with install status, version, custom agent form for any CLI tool |
|
||||
| 🔧 **Custom Model `apiFormat` Routing** | Custom models with `apiFormat: "responses"` now correctly route to the Responses API translator |
|
||||
| 🏢 **Codex Workspace Isolation** | Multiple Codex workspaces per email — OAuth correctly separates connections by workspace ID |
|
||||
| 🔄 **Electron Auto-Update** | Desktop app checks for updates + auto-install on restart |
|
||||
|
||||
### 🤖 Ingyenes mesterséges intelligencia szolgáltató kedvenc kódoló ügynökei számára
|
||||
|
||||
_Csatlakoztasson bármilyen mesterséges intelligencia-alapú IDE-t vagy CLI-eszközt az OmniRoute-on keresztül – ingyenes API-átjáró a korlátlan kódoláshoz._
|
||||
@@ -118,6 +96,38 @@ _Csatlakoztasson bármilyen mesterséges intelligencia-alapú IDE-t vagy CLI-esz
|
||||
|
||||
---
|
||||
|
||||
### 🆕 What's New in v3.0.0
|
||||
|
||||
| Area | Change |
|
||||
| --- | --- |
|
||||
| 🔒 **CodeQL Security** | Fixed 10+ CodeQL alerts: polynomial-redos, insecure-randomness, shell-injection |
|
||||
| ✅ **Route Validation** | All 176 API routes validated with Zod schemas + `validateBody()` |
|
||||
| 🐛 **omniModel Tag Leak** | Internal `<omniModel>` tags no longer leak to clients in SSE streams (#585) |
|
||||
| 🔑 **Registered Keys API** | Auto-provision API keys via `POST /api/v1/registered-keys` with quota enforcement |
|
||||
| 🎨 **Provider Icons** | 130+ provider logos via `@lobehub/icons` (SVG) with PNG fallback |
|
||||
| 🔄 **Model Auto-Sync** | 24h scheduler refreshes model lists for 16 providers |
|
||||
| 🌐 **OpenCode Zen/Go** | Two new providers: free tier + subscription tier |
|
||||
| 🔧 **926 Tests** | Full test suite passes with 0 failures |
|
||||
|
||||
|
||||
|
||||
### 🆕 What's New in v3.0.0
|
||||
|
||||
| Area | Change |
|
||||
| --- | --- |
|
||||
| 🔒 **CodeQL Security** | Fixed 10+ CodeQL alerts: polynomial-redos, insecure-randomness, shell-injection |
|
||||
| ✅ **Route Validation** | All 176 API routes validated with Zod schemas + `validateBody()` |
|
||||
| 🐛 **omniModel Tag Leak** | Internal `<omniModel>` tags no longer leak to clients in SSE streams (#585) |
|
||||
| 🔑 **Registered Keys API** | Auto-provision API keys via `POST /api/v1/registered-keys` with quota enforcement |
|
||||
| 🎨 **Provider Icons** | 130+ provider logos via `@lobehub/icons` (SVG) with PNG fallback |
|
||||
| 🔄 **Model Auto-Sync** | 24h scheduler refreshes model lists for 16 providers |
|
||||
| 🌐 **OpenCode Zen/Go** | Two new providers: free tier + subscription tier |
|
||||
| 🔧 **926 Tests** | Full test suite passes with 0 failures |
|
||||
|
||||
---
|
||||
|
||||
|
||||
|
||||
[](https://www.npmjs.com/package/omniroute)
|
||||
[](https://hub.docker.com/r/diegosouzapw/omniroute)
|
||||
[](https://github.com/diegosouzapw/OmniRoute/blob/main/LICENSE)
|
||||
|
||||
@@ -11,28 +11,6 @@ _Proksi API universal Anda — satu titik akhir, 36+ penyedia, tanpa waktu henti
|
||||
|
||||
---
|
||||
|
||||
### 🆕 What's New in v2.7.0
|
||||
|
||||
- **Pluggable RouterStrategy** — rules, cost, and latency routing strategies
|
||||
- **Multilingual intent detection** — routing scoring in 30+ languages
|
||||
- **Request deduplication** — prevent duplicate API calls via content hash
|
||||
- **New providers:** Grok-4 Fast (xAI), GLM-5 / Z.AI, MiniMax M2.5, Kimi K2.5
|
||||
- **Updated pricing:** Grok-4 Fast $0.20/$0.50/M, GLM-5 $0.50/M, MiniMax M2.5 $0.30/M
|
||||
|
||||
---
|
||||
|
||||
### 🚀 New in v2.0.9+ — Playground, CLI Fingerprints & ACP
|
||||
|
||||
| Feature | What It Does |
|
||||
| ------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| 🎮 **Model Playground** | Dashboard page to test any model directly — provider/model/endpoint selectors, Monaco Editor, streaming, abort, timing |
|
||||
| 🔏 **CLI Fingerprint Matching** | Per-provider header/body ordering to match native CLI signatures — toggle per provider in Settings > Security. **Your proxy IP is preserved** |
|
||||
| 🤝 **ACP Support (Agent Client Protocol)** | CLI agent discovery (Codex, Claude, Goose, Gemini CLI, OpenClaw), process spawner, `/api/acp/agents` endpoint |
|
||||
| 🤖 **ACP Agents Dashboard** | Debug > Agents page — grid of 14 agents with install status, version, custom agent form for any CLI tool |
|
||||
| 🔧 **Custom Model `apiFormat` Routing** | Custom models with `apiFormat: "responses"` now correctly route to the Responses API translator |
|
||||
| 🏢 **Codex Workspace Isolation** | Multiple Codex workspaces per email — OAuth correctly separates connections by workspace ID |
|
||||
| 🔄 **Electron Auto-Update** | Desktop app checks for updates + auto-install on restart |
|
||||
|
||||
### 🤖 Penyedia AI gratis untuk agen coding favorit Anda
|
||||
|
||||
_Hubungkan alat IDE atau CLI apa pun yang didukung AI melalui OmniRoute — gerbang API gratis untuk pengkodean tanpa batas._
|
||||
@@ -118,6 +96,38 @@ _Hubungkan alat IDE atau CLI apa pun yang didukung AI melalui OmniRoute — gerb
|
||||
|
||||
---
|
||||
|
||||
### 🆕 What's New in v3.0.0
|
||||
|
||||
| Area | Change |
|
||||
| --- | --- |
|
||||
| 🔒 **CodeQL Security** | Fixed 10+ CodeQL alerts: polynomial-redos, insecure-randomness, shell-injection |
|
||||
| ✅ **Route Validation** | All 176 API routes validated with Zod schemas + `validateBody()` |
|
||||
| 🐛 **omniModel Tag Leak** | Internal `<omniModel>` tags no longer leak to clients in SSE streams (#585) |
|
||||
| 🔑 **Registered Keys API** | Auto-provision API keys via `POST /api/v1/registered-keys` with quota enforcement |
|
||||
| 🎨 **Provider Icons** | 130+ provider logos via `@lobehub/icons` (SVG) with PNG fallback |
|
||||
| 🔄 **Model Auto-Sync** | 24h scheduler refreshes model lists for 16 providers |
|
||||
| 🌐 **OpenCode Zen/Go** | Two new providers: free tier + subscription tier |
|
||||
| 🔧 **926 Tests** | Full test suite passes with 0 failures |
|
||||
|
||||
|
||||
|
||||
### 🆕 What's New in v3.0.0
|
||||
|
||||
| Area | Change |
|
||||
| --- | --- |
|
||||
| 🔒 **CodeQL Security** | Fixed 10+ CodeQL alerts: polynomial-redos, insecure-randomness, shell-injection |
|
||||
| ✅ **Route Validation** | All 176 API routes validated with Zod schemas + `validateBody()` |
|
||||
| 🐛 **omniModel Tag Leak** | Internal `<omniModel>` tags no longer leak to clients in SSE streams (#585) |
|
||||
| 🔑 **Registered Keys API** | Auto-provision API keys via `POST /api/v1/registered-keys` with quota enforcement |
|
||||
| 🎨 **Provider Icons** | 130+ provider logos via `@lobehub/icons` (SVG) with PNG fallback |
|
||||
| 🔄 **Model Auto-Sync** | 24h scheduler refreshes model lists for 16 providers |
|
||||
| 🌐 **OpenCode Zen/Go** | Two new providers: free tier + subscription tier |
|
||||
| 🔧 **926 Tests** | Full test suite passes with 0 failures |
|
||||
|
||||
---
|
||||
|
||||
|
||||
|
||||
[](https://www.npmjs.com/package/omniroute)
|
||||
[](https://hub.docker.com/r/diegosouzapw/omniroute)
|
||||
[](https://github.com/diegosouzapw/OmniRoute/blob/main/LICENSE)
|
||||
|
||||
@@ -13,28 +13,6 @@ _आपका सार्वभौमिक एपीआई प्रॉक्
|
||||
|
||||
---
|
||||
|
||||
### 🆕 What's New in v2.7.0
|
||||
|
||||
- **Pluggable RouterStrategy** — rules, cost, and latency routing strategies
|
||||
- **Multilingual intent detection** — routing scoring in 30+ languages
|
||||
- **Request deduplication** — prevent duplicate API calls via content hash
|
||||
- **New providers:** Grok-4 Fast (xAI), GLM-5 / Z.AI, MiniMax M2.5, Kimi K2.5
|
||||
- **Updated pricing:** Grok-4 Fast $0.20/$0.50/M, GLM-5 $0.50/M, MiniMax M2.5 $0.30/M
|
||||
|
||||
---
|
||||
|
||||
### 🚀 New in v2.0.9+ — Playground, CLI Fingerprints & ACP
|
||||
|
||||
| Feature | What It Does |
|
||||
| ------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| 🎮 **Model Playground** | Dashboard page to test any model directly — provider/model/endpoint selectors, Monaco Editor, streaming, abort, timing |
|
||||
| 🔏 **CLI Fingerprint Matching** | Per-provider header/body ordering to match native CLI signatures — toggle per provider in Settings > Security. **Your proxy IP is preserved** |
|
||||
| 🤝 **ACP Support (Agent Client Protocol)** | CLI agent discovery (Codex, Claude, Goose, Gemini CLI, OpenClaw), process spawner, `/api/acp/agents` endpoint |
|
||||
| 🤖 **ACP Agents Dashboard** | Debug > Agents page — grid of 14 agents with install status, version, custom agent form for any CLI tool |
|
||||
| 🔧 **Custom Model `apiFormat` Routing** | Custom models with `apiFormat: "responses"` now correctly route to the Responses API translator |
|
||||
| 🏢 **Codex Workspace Isolation** | Multiple Codex workspaces per email — OAuth correctly separates connections by workspace ID |
|
||||
| 🔄 **Electron Auto-Update** | Desktop app checks for updates + auto-install on restart |
|
||||
|
||||
### 🤖 आपके पसंदीदा कोडिंग एजेंटों के लिए निःशुल्क एआई प्रदाता
|
||||
|
||||
_OmniRoute के माध्यम से किसी भी AI-संचालित IDE या CLI टूल को कनेक्ट करें - असीमित कोडिंग के लिए निःशुल्क API गेटवे।_
|
||||
@@ -43,6 +21,38 @@ _OmniRoute के माध्यम से किसी भी AI-संचा
|
||||
|
||||
---
|
||||
|
||||
### 🆕 What's New in v3.0.0
|
||||
|
||||
| Area | Change |
|
||||
| --- | --- |
|
||||
| 🔒 **CodeQL Security** | Fixed 10+ CodeQL alerts: polynomial-redos, insecure-randomness, shell-injection |
|
||||
| ✅ **Route Validation** | All 176 API routes validated with Zod schemas + `validateBody()` |
|
||||
| 🐛 **omniModel Tag Leak** | Internal `<omniModel>` tags no longer leak to clients in SSE streams (#585) |
|
||||
| 🔑 **Registered Keys API** | Auto-provision API keys via `POST /api/v1/registered-keys` with quota enforcement |
|
||||
| 🎨 **Provider Icons** | 130+ provider logos via `@lobehub/icons` (SVG) with PNG fallback |
|
||||
| 🔄 **Model Auto-Sync** | 24h scheduler refreshes model lists for 16 providers |
|
||||
| 🌐 **OpenCode Zen/Go** | Two new providers: free tier + subscription tier |
|
||||
| 🔧 **926 Tests** | Full test suite passes with 0 failures |
|
||||
|
||||
|
||||
|
||||
### 🆕 What's New in v3.0.0
|
||||
|
||||
| Area | Change |
|
||||
| --- | --- |
|
||||
| 🔒 **CodeQL Security** | Fixed 10+ CodeQL alerts: polynomial-redos, insecure-randomness, shell-injection |
|
||||
| ✅ **Route Validation** | All 176 API routes validated with Zod schemas + `validateBody()` |
|
||||
| 🐛 **omniModel Tag Leak** | Internal `<omniModel>` tags no longer leak to clients in SSE streams (#585) |
|
||||
| 🔑 **Registered Keys API** | Auto-provision API keys via `POST /api/v1/registered-keys` with quota enforcement |
|
||||
| 🎨 **Provider Icons** | 130+ provider logos via `@lobehub/icons` (SVG) with PNG fallback |
|
||||
| 🔄 **Model Auto-Sync** | 24h scheduler refreshes model lists for 16 providers |
|
||||
| 🌐 **OpenCode Zen/Go** | Two new providers: free tier + subscription tier |
|
||||
| 🔧 **926 Tests** | Full test suite passes with 0 failures |
|
||||
|
||||
---
|
||||
|
||||
|
||||
|
||||
[](https://www.npmjs.com/package/omniroute)
|
||||
[](https://hub.docker.com/r/diegosouzapw/omniroute)
|
||||
[](https://github.com/diegosouzapw/OmniRoute/blob/main/LICENSE)
|
||||
|
||||
@@ -5,34 +5,12 @@
|
||||
|
||||
### Non smettere mai di programmare. Routing intelligente verso **modelli IA GRATUITI e economici** con fallback automatico.
|
||||
|
||||
_Il tuo proxy API universale — un endpoint, 36+ provider, zero downtime._
|
||||
_Il tuo proxy API universale — un endpoint, 67+ provider, zero downtime._
|
||||
|
||||
**Chat Completions • Embeddings • Generazione Immagini • Audio • Reranking • 100% TypeScript**
|
||||
|
||||
---
|
||||
|
||||
### 🆕 Novità in v2.7.0
|
||||
|
||||
- **RouterStrategy estensibile** — strategie per regole, costo e latenza
|
||||
- **Rilevamento intento multilingue** — scoring di routing in 30+ lingue
|
||||
- **Deduplicazione richieste** — evita chiamate duplicate tramite hash del contenuto
|
||||
- **Nuovi provider:** Grok-4 Fast (xAI), GLM-5 / Z.AI, MiniMax M2.5, Kimi K2.5
|
||||
- **Prezzi aggiornati:** Grok-4 Fast $0.20/$0.50/M, GLM-5 $0.50/M, MiniMax M2.5 $0.30/M
|
||||
|
||||
---
|
||||
|
||||
### 🚀 New in v2.0.9+ — Playground, CLI Fingerprints & ACP
|
||||
|
||||
| Feature | What It Does |
|
||||
| ------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| 🎮 **Model Playground** | Dashboard page to test any model directly — provider/model/endpoint selectors, Monaco Editor, streaming, abort, timing |
|
||||
| 🔏 **CLI Fingerprint Matching** | Per-provider header/body ordering to match native CLI signatures — toggle per provider in Settings > Security. **Your proxy IP is preserved** |
|
||||
| 🤝 **ACP Support (Agent Client Protocol)** | CLI agent discovery (Codex, Claude, Goose, Gemini CLI, OpenClaw), process spawner, `/api/acp/agents` endpoint |
|
||||
| 🤖 **ACP Agents Dashboard** | Debug > Agents page — grid of 14 agents with install status, version, custom agent form for any CLI tool |
|
||||
| 🔧 **Custom Model `apiFormat` Routing** | Custom models with `apiFormat: "responses"` now correctly route to the Responses API translator |
|
||||
| 🏢 **Codex Workspace Isolation** | Multiple Codex workspaces per email — OAuth correctly separates connections by workspace ID |
|
||||
| 🔄 **Electron Auto-Update** | Desktop app checks for updates + auto-install on restart |
|
||||
|
||||
### 🤖 Provider IA gratuito per i tuoi agenti di programmazione preferiti
|
||||
|
||||
_Connetti qualsiasi IDE o strumento CLI con IA tramite OmniRoute — gateway API gratuito per programmazione illimitata._
|
||||
@@ -118,6 +96,38 @@ _Connetti qualsiasi IDE o strumento CLI con IA tramite OmniRoute — gateway API
|
||||
|
||||
---
|
||||
|
||||
### 🆕 What's New in v3.0.0
|
||||
|
||||
| Area | Change |
|
||||
| --- | --- |
|
||||
| 🔒 **CodeQL Security** | Fixed 10+ CodeQL alerts: polynomial-redos, insecure-randomness, shell-injection |
|
||||
| ✅ **Route Validation** | All 176 API routes validated with Zod schemas + `validateBody()` |
|
||||
| 🐛 **omniModel Tag Leak** | Internal `<omniModel>` tags no longer leak to clients in SSE streams (#585) |
|
||||
| 🔑 **Registered Keys API** | Auto-provision API keys via `POST /api/v1/registered-keys` with quota enforcement |
|
||||
| 🎨 **Provider Icons** | 130+ provider logos via `@lobehub/icons` (SVG) with PNG fallback |
|
||||
| 🔄 **Model Auto-Sync** | 24h scheduler refreshes model lists for 16 providers |
|
||||
| 🌐 **OpenCode Zen/Go** | Two new providers: free tier + subscription tier |
|
||||
| 🔧 **926 Tests** | Full test suite passes with 0 failures |
|
||||
|
||||
|
||||
|
||||
### 🆕 What's New in v3.0.0
|
||||
|
||||
| Area | Change |
|
||||
| --- | --- |
|
||||
| 🔒 **CodeQL Security** | Fixed 10+ CodeQL alerts: polynomial-redos, insecure-randomness, shell-injection |
|
||||
| ✅ **Route Validation** | All 176 API routes validated with Zod schemas + `validateBody()` |
|
||||
| 🐛 **omniModel Tag Leak** | Internal `<omniModel>` tags no longer leak to clients in SSE streams (#585) |
|
||||
| 🔑 **Registered Keys API** | Auto-provision API keys via `POST /api/v1/registered-keys` with quota enforcement |
|
||||
| 🎨 **Provider Icons** | 130+ provider logos via `@lobehub/icons` (SVG) with PNG fallback |
|
||||
| 🔄 **Model Auto-Sync** | 24h scheduler refreshes model lists for 16 providers |
|
||||
| 🌐 **OpenCode Zen/Go** | Two new providers: free tier + subscription tier |
|
||||
| 🔧 **926 Tests** | Full test suite passes with 0 failures |
|
||||
|
||||
---
|
||||
|
||||
|
||||
|
||||
[](https://www.npmjs.com/package/omniroute)
|
||||
[](https://hub.docker.com/r/diegosouzapw/omniroute)
|
||||
[](https://github.com/diegosouzapw/OmniRoute/blob/main/LICENSE)
|
||||
|
||||
@@ -11,28 +11,6 @@ _ユニバーサル API プロキシ — 1 つのエンドポイント、36 以
|
||||
|
||||
---
|
||||
|
||||
### 🆕 v2.7.0 の新機能
|
||||
|
||||
- **プラガブル RouterStrategy** — ルール・コスト・レイテンシ戦略をサポート
|
||||
- **多言語インテント検出** — 30以上の言語でルーティングスコアリング
|
||||
- **リクエスト重複排除** — コンテンツハッシュで重複 API 呼び出しを防止
|
||||
- **新しいプロバイダー:** Grok-4 Fast (xAI)、GLM-5 / Z.AI、MiniMax M2.5、Kimi K2.5
|
||||
- **価格更新:** Grok-4 Fast $0.20/$0.50/M、GLM-5 $0.50/M、MiniMax M2.5 $0.30/M
|
||||
|
||||
---
|
||||
|
||||
### 🚀 New in v2.0.9+ — Playground, CLI Fingerprints & ACP
|
||||
|
||||
| Feature | What It Does |
|
||||
| ------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| 🎮 **Model Playground** | Dashboard page to test any model directly — provider/model/endpoint selectors, Monaco Editor, streaming, abort, timing |
|
||||
| 🔏 **CLI Fingerprint Matching** | Per-provider header/body ordering to match native CLI signatures — toggle per provider in Settings > Security. **Your proxy IP is preserved** |
|
||||
| 🤝 **ACP Support (Agent Client Protocol)** | CLI agent discovery (Codex, Claude, Goose, Gemini CLI, OpenClaw), process spawner, `/api/acp/agents` endpoint |
|
||||
| 🤖 **ACP Agents Dashboard** | Debug > Agents page — grid of 14 agents with install status, version, custom agent form for any CLI tool |
|
||||
| 🔧 **Custom Model `apiFormat` Routing** | Custom models with `apiFormat: "responses"` now correctly route to the Responses API translator |
|
||||
| 🏢 **Codex Workspace Isolation** | Multiple Codex workspaces per email — OAuth correctly separates connections by workspace ID |
|
||||
| 🔄 **Electron Auto-Update** | Desktop app checks for updates + auto-install on restart |
|
||||
|
||||
### 🤖 お気に入りのコーディング エージェント向けの無料 AI プロバイダー
|
||||
|
||||
_AI を活用した IDE または CLI ツールを、無制限のコーディングのための無料 API ゲートウェイである OmniRoute 経由で接続します。_
|
||||
@@ -118,6 +96,38 @@ _AI を活用した IDE または CLI ツールを、無制限のコーディン
|
||||
|
||||
---
|
||||
|
||||
### 🆕 What's New in v3.0.0
|
||||
|
||||
| Area | Change |
|
||||
| --- | --- |
|
||||
| 🔒 **CodeQL Security** | Fixed 10+ CodeQL alerts: polynomial-redos, insecure-randomness, shell-injection |
|
||||
| ✅ **Route Validation** | All 176 API routes validated with Zod schemas + `validateBody()` |
|
||||
| 🐛 **omniModel Tag Leak** | Internal `<omniModel>` tags no longer leak to clients in SSE streams (#585) |
|
||||
| 🔑 **Registered Keys API** | Auto-provision API keys via `POST /api/v1/registered-keys` with quota enforcement |
|
||||
| 🎨 **Provider Icons** | 130+ provider logos via `@lobehub/icons` (SVG) with PNG fallback |
|
||||
| 🔄 **Model Auto-Sync** | 24h scheduler refreshes model lists for 16 providers |
|
||||
| 🌐 **OpenCode Zen/Go** | Two new providers: free tier + subscription tier |
|
||||
| 🔧 **926 Tests** | Full test suite passes with 0 failures |
|
||||
|
||||
|
||||
|
||||
### 🆕 What's New in v3.0.0
|
||||
|
||||
| Area | Change |
|
||||
| --- | --- |
|
||||
| 🔒 **CodeQL Security** | Fixed 10+ CodeQL alerts: polynomial-redos, insecure-randomness, shell-injection |
|
||||
| ✅ **Route Validation** | All 176 API routes validated with Zod schemas + `validateBody()` |
|
||||
| 🐛 **omniModel Tag Leak** | Internal `<omniModel>` tags no longer leak to clients in SSE streams (#585) |
|
||||
| 🔑 **Registered Keys API** | Auto-provision API keys via `POST /api/v1/registered-keys` with quota enforcement |
|
||||
| 🎨 **Provider Icons** | 130+ provider logos via `@lobehub/icons` (SVG) with PNG fallback |
|
||||
| 🔄 **Model Auto-Sync** | 24h scheduler refreshes model lists for 16 providers |
|
||||
| 🌐 **OpenCode Zen/Go** | Two new providers: free tier + subscription tier |
|
||||
| 🔧 **926 Tests** | Full test suite passes with 0 failures |
|
||||
|
||||
---
|
||||
|
||||
|
||||
|
||||
[](https://www.npmjs.com/package/omniroute)
|
||||
[](https://hub.docker.com/r/diegosouzapw/omniroute)
|
||||
[](https://github.com/diegosouzapw/OmniRoute/blob/main/LICENSE)
|
||||
|
||||
@@ -11,28 +11,6 @@ _범용 API 프록시 — 하나의 엔드포인트, 36개 이상의 공급자,
|
||||
|
||||
---
|
||||
|
||||
### 🆕 v2.7.0 새로운 기능
|
||||
|
||||
- **플러그형 RouterStrategy** — 규칙, 비용, 지연 전략 지원
|
||||
- **다국어 의도 감지** — 30개 이상 언어로 라우팅 스코어링
|
||||
- **요청 중복 제거** — 콘텐츠 해시로 중복 API 호출 방지
|
||||
- **새 공급자:** Grok-4 Fast (xAI), GLM-5 / Z.AI, MiniMax M2.5, Kimi K2.5
|
||||
- **가격 업데이트:** Grok-4 Fast $0.20/$0.50/M, GLM-5 $0.50/M, MiniMax M2.5 $0.30/M
|
||||
|
||||
---
|
||||
|
||||
### 🚀 New in v2.0.9+ — Playground, CLI Fingerprints & ACP
|
||||
|
||||
| Feature | What It Does |
|
||||
| ------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| 🎮 **Model Playground** | Dashboard page to test any model directly — provider/model/endpoint selectors, Monaco Editor, streaming, abort, timing |
|
||||
| 🔏 **CLI Fingerprint Matching** | Per-provider header/body ordering to match native CLI signatures — toggle per provider in Settings > Security. **Your proxy IP is preserved** |
|
||||
| 🤝 **ACP Support (Agent Client Protocol)** | CLI agent discovery (Codex, Claude, Goose, Gemini CLI, OpenClaw), process spawner, `/api/acp/agents` endpoint |
|
||||
| 🤖 **ACP Agents Dashboard** | Debug > Agents page — grid of 14 agents with install status, version, custom agent form for any CLI tool |
|
||||
| 🔧 **Custom Model `apiFormat` Routing** | Custom models with `apiFormat: "responses"` now correctly route to the Responses API translator |
|
||||
| 🏢 **Codex Workspace Isolation** | Multiple Codex workspaces per email — OAuth correctly separates connections by workspace ID |
|
||||
| 🔄 **Electron Auto-Update** | Desktop app checks for updates + auto-install on restart |
|
||||
|
||||
### 🤖 좋아하는 코딩 에이전트를 위한 무료 AI 제공업체
|
||||
|
||||
_무제한 코딩을 위한 무료 API 게이트웨이인 OmniRoute를 통해 AI 기반 IDE 또는 CLI 도구를 연결하세요._
|
||||
@@ -118,6 +96,38 @@ _무제한 코딩을 위한 무료 API 게이트웨이인 OmniRoute를 통해 AI
|
||||
|
||||
---
|
||||
|
||||
### 🆕 What's New in v3.0.0
|
||||
|
||||
| Area | Change |
|
||||
| --- | --- |
|
||||
| 🔒 **CodeQL Security** | Fixed 10+ CodeQL alerts: polynomial-redos, insecure-randomness, shell-injection |
|
||||
| ✅ **Route Validation** | All 176 API routes validated with Zod schemas + `validateBody()` |
|
||||
| 🐛 **omniModel Tag Leak** | Internal `<omniModel>` tags no longer leak to clients in SSE streams (#585) |
|
||||
| 🔑 **Registered Keys API** | Auto-provision API keys via `POST /api/v1/registered-keys` with quota enforcement |
|
||||
| 🎨 **Provider Icons** | 130+ provider logos via `@lobehub/icons` (SVG) with PNG fallback |
|
||||
| 🔄 **Model Auto-Sync** | 24h scheduler refreshes model lists for 16 providers |
|
||||
| 🌐 **OpenCode Zen/Go** | Two new providers: free tier + subscription tier |
|
||||
| 🔧 **926 Tests** | Full test suite passes with 0 failures |
|
||||
|
||||
|
||||
|
||||
### 🆕 What's New in v3.0.0
|
||||
|
||||
| Area | Change |
|
||||
| --- | --- |
|
||||
| 🔒 **CodeQL Security** | Fixed 10+ CodeQL alerts: polynomial-redos, insecure-randomness, shell-injection |
|
||||
| ✅ **Route Validation** | All 176 API routes validated with Zod schemas + `validateBody()` |
|
||||
| 🐛 **omniModel Tag Leak** | Internal `<omniModel>` tags no longer leak to clients in SSE streams (#585) |
|
||||
| 🔑 **Registered Keys API** | Auto-provision API keys via `POST /api/v1/registered-keys` with quota enforcement |
|
||||
| 🎨 **Provider Icons** | 130+ provider logos via `@lobehub/icons` (SVG) with PNG fallback |
|
||||
| 🔄 **Model Auto-Sync** | 24h scheduler refreshes model lists for 16 providers |
|
||||
| 🌐 **OpenCode Zen/Go** | Two new providers: free tier + subscription tier |
|
||||
| 🔧 **926 Tests** | Full test suite passes with 0 failures |
|
||||
|
||||
---
|
||||
|
||||
|
||||
|
||||
[](https://www.npmjs.com/package/omniroute)
|
||||
[](https://hub.docker.com/r/diegosouzapw/omniroute)
|
||||
[](https://github.com/diegosouzapw/OmniRoute/blob/main/LICENSE)
|
||||
|
||||
@@ -11,28 +11,6 @@ _Proksi API universal anda — satu titik akhir, 36+ pembekal, masa henti sifar.
|
||||
|
||||
---
|
||||
|
||||
### 🆕 What's New in v2.7.0
|
||||
|
||||
- **Pluggable RouterStrategy** — rules, cost, and latency routing strategies
|
||||
- **Multilingual intent detection** — routing scoring in 30+ languages
|
||||
- **Request deduplication** — prevent duplicate API calls via content hash
|
||||
- **New providers:** Grok-4 Fast (xAI), GLM-5 / Z.AI, MiniMax M2.5, Kimi K2.5
|
||||
- **Updated pricing:** Grok-4 Fast $0.20/$0.50/M, GLM-5 $0.50/M, MiniMax M2.5 $0.30/M
|
||||
|
||||
---
|
||||
|
||||
### 🚀 New in v2.0.9+ — Playground, CLI Fingerprints & ACP
|
||||
|
||||
| Feature | What It Does |
|
||||
| ------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| 🎮 **Model Playground** | Dashboard page to test any model directly — provider/model/endpoint selectors, Monaco Editor, streaming, abort, timing |
|
||||
| 🔏 **CLI Fingerprint Matching** | Per-provider header/body ordering to match native CLI signatures — toggle per provider in Settings > Security. **Your proxy IP is preserved** |
|
||||
| 🤝 **ACP Support (Agent Client Protocol)** | CLI agent discovery (Codex, Claude, Goose, Gemini CLI, OpenClaw), process spawner, `/api/acp/agents` endpoint |
|
||||
| 🤖 **ACP Agents Dashboard** | Debug > Agents page — grid of 14 agents with install status, version, custom agent form for any CLI tool |
|
||||
| 🔧 **Custom Model `apiFormat` Routing** | Custom models with `apiFormat: "responses"` now correctly route to the Responses API translator |
|
||||
| 🏢 **Codex Workspace Isolation** | Multiple Codex workspaces per email — OAuth correctly separates connections by workspace ID |
|
||||
| 🔄 **Electron Auto-Update** | Desktop app checks for updates + auto-install on restart |
|
||||
|
||||
### 🤖 Pembekal AI percuma untuk ejen pengekodan kegemaran anda
|
||||
|
||||
_Sambungkan mana-mana alat IDE atau CLI berkuasa AI melalui OmniRoute — get laluan API percuma untuk pengekodan tanpa had._
|
||||
@@ -118,6 +96,38 @@ _Sambungkan mana-mana alat IDE atau CLI berkuasa AI melalui OmniRoute — get la
|
||||
|
||||
---
|
||||
|
||||
### 🆕 What's New in v3.0.0
|
||||
|
||||
| Area | Change |
|
||||
| --- | --- |
|
||||
| 🔒 **CodeQL Security** | Fixed 10+ CodeQL alerts: polynomial-redos, insecure-randomness, shell-injection |
|
||||
| ✅ **Route Validation** | All 176 API routes validated with Zod schemas + `validateBody()` |
|
||||
| 🐛 **omniModel Tag Leak** | Internal `<omniModel>` tags no longer leak to clients in SSE streams (#585) |
|
||||
| 🔑 **Registered Keys API** | Auto-provision API keys via `POST /api/v1/registered-keys` with quota enforcement |
|
||||
| 🎨 **Provider Icons** | 130+ provider logos via `@lobehub/icons` (SVG) with PNG fallback |
|
||||
| 🔄 **Model Auto-Sync** | 24h scheduler refreshes model lists for 16 providers |
|
||||
| 🌐 **OpenCode Zen/Go** | Two new providers: free tier + subscription tier |
|
||||
| 🔧 **926 Tests** | Full test suite passes with 0 failures |
|
||||
|
||||
|
||||
|
||||
### 🆕 What's New in v3.0.0
|
||||
|
||||
| Area | Change |
|
||||
| --- | --- |
|
||||
| 🔒 **CodeQL Security** | Fixed 10+ CodeQL alerts: polynomial-redos, insecure-randomness, shell-injection |
|
||||
| ✅ **Route Validation** | All 176 API routes validated with Zod schemas + `validateBody()` |
|
||||
| 🐛 **omniModel Tag Leak** | Internal `<omniModel>` tags no longer leak to clients in SSE streams (#585) |
|
||||
| 🔑 **Registered Keys API** | Auto-provision API keys via `POST /api/v1/registered-keys` with quota enforcement |
|
||||
| 🎨 **Provider Icons** | 130+ provider logos via `@lobehub/icons` (SVG) with PNG fallback |
|
||||
| 🔄 **Model Auto-Sync** | 24h scheduler refreshes model lists for 16 providers |
|
||||
| 🌐 **OpenCode Zen/Go** | Two new providers: free tier + subscription tier |
|
||||
| 🔧 **926 Tests** | Full test suite passes with 0 failures |
|
||||
|
||||
---
|
||||
|
||||
|
||||
|
||||
[](https://www.npmjs.com/package/omniroute)
|
||||
[](https://hub.docker.com/r/diegosouzapw/omniroute)
|
||||
[](https://github.com/diegosouzapw/OmniRoute/blob/main/LICENSE)
|
||||
|
||||
@@ -11,28 +11,6 @@ _Uw universele API-proxy: één eindpunt, meer dan 36 providers, geen downtime._
|
||||
|
||||
---
|
||||
|
||||
### 🆕 What's New in v2.7.0
|
||||
|
||||
- **Pluggable RouterStrategy** — rules, cost, and latency routing strategies
|
||||
- **Multilingual intent detection** — routing scoring in 30+ languages
|
||||
- **Request deduplication** — prevent duplicate API calls via content hash
|
||||
- **New providers:** Grok-4 Fast (xAI), GLM-5 / Z.AI, MiniMax M2.5, Kimi K2.5
|
||||
- **Updated pricing:** Grok-4 Fast $0.20/$0.50/M, GLM-5 $0.50/M, MiniMax M2.5 $0.30/M
|
||||
|
||||
---
|
||||
|
||||
### 🚀 New in v2.0.9+ — Playground, CLI Fingerprints & ACP
|
||||
|
||||
| Feature | What It Does |
|
||||
| ------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| 🎮 **Model Playground** | Dashboard page to test any model directly — provider/model/endpoint selectors, Monaco Editor, streaming, abort, timing |
|
||||
| 🔏 **CLI Fingerprint Matching** | Per-provider header/body ordering to match native CLI signatures — toggle per provider in Settings > Security. **Your proxy IP is preserved** |
|
||||
| 🤝 **ACP Support (Agent Client Protocol)** | CLI agent discovery (Codex, Claude, Goose, Gemini CLI, OpenClaw), process spawner, `/api/acp/agents` endpoint |
|
||||
| 🤖 **ACP Agents Dashboard** | Debug > Agents page — grid of 14 agents with install status, version, custom agent form for any CLI tool |
|
||||
| 🔧 **Custom Model `apiFormat` Routing** | Custom models with `apiFormat: "responses"` now correctly route to the Responses API translator |
|
||||
| 🏢 **Codex Workspace Isolation** | Multiple Codex workspaces per email — OAuth correctly separates connections by workspace ID |
|
||||
| 🔄 **Electron Auto-Update** | Desktop app checks for updates + auto-install on restart |
|
||||
|
||||
### 🤖 Gratis AI-provider voor uw favoriete codeeragenten
|
||||
|
||||
_Verbind elke AI-aangedreven IDE- of CLI-tool via OmniRoute: gratis API-gateway voor onbeperkte codering._
|
||||
@@ -118,6 +96,38 @@ _Verbind elke AI-aangedreven IDE- of CLI-tool via OmniRoute: gratis API-gateway
|
||||
|
||||
---
|
||||
|
||||
### 🆕 What's New in v3.0.0
|
||||
|
||||
| Area | Change |
|
||||
| --- | --- |
|
||||
| 🔒 **CodeQL Security** | Fixed 10+ CodeQL alerts: polynomial-redos, insecure-randomness, shell-injection |
|
||||
| ✅ **Route Validation** | All 176 API routes validated with Zod schemas + `validateBody()` |
|
||||
| 🐛 **omniModel Tag Leak** | Internal `<omniModel>` tags no longer leak to clients in SSE streams (#585) |
|
||||
| 🔑 **Registered Keys API** | Auto-provision API keys via `POST /api/v1/registered-keys` with quota enforcement |
|
||||
| 🎨 **Provider Icons** | 130+ provider logos via `@lobehub/icons` (SVG) with PNG fallback |
|
||||
| 🔄 **Model Auto-Sync** | 24h scheduler refreshes model lists for 16 providers |
|
||||
| 🌐 **OpenCode Zen/Go** | Two new providers: free tier + subscription tier |
|
||||
| 🔧 **926 Tests** | Full test suite passes with 0 failures |
|
||||
|
||||
|
||||
|
||||
### 🆕 What's New in v3.0.0
|
||||
|
||||
| Area | Change |
|
||||
| --- | --- |
|
||||
| 🔒 **CodeQL Security** | Fixed 10+ CodeQL alerts: polynomial-redos, insecure-randomness, shell-injection |
|
||||
| ✅ **Route Validation** | All 176 API routes validated with Zod schemas + `validateBody()` |
|
||||
| 🐛 **omniModel Tag Leak** | Internal `<omniModel>` tags no longer leak to clients in SSE streams (#585) |
|
||||
| 🔑 **Registered Keys API** | Auto-provision API keys via `POST /api/v1/registered-keys` with quota enforcement |
|
||||
| 🎨 **Provider Icons** | 130+ provider logos via `@lobehub/icons` (SVG) with PNG fallback |
|
||||
| 🔄 **Model Auto-Sync** | 24h scheduler refreshes model lists for 16 providers |
|
||||
| 🌐 **OpenCode Zen/Go** | Two new providers: free tier + subscription tier |
|
||||
| 🔧 **926 Tests** | Full test suite passes with 0 failures |
|
||||
|
||||
---
|
||||
|
||||
|
||||
|
||||
[](https://www.npmjs.com/package/omniroute)
|
||||
[](https://hub.docker.com/r/diegosouzapw/omniroute)
|
||||
[](https://github.com/diegosouzapw/OmniRoute/blob/main/LICENSE)
|
||||
@@ -246,7 +256,7 @@ OpenAI gebruikt het ene formaat, Claude (Anthropic) gebruikt een ander, Gemini n
|
||||
|
||||
**Hoe OmniRoute het oplost:**
|
||||
|
||||
- **Unified Endpoint** — Eén enkele `http://localhost:20128/v1` dient als proxy voor alle 36+ providers
|
||||
- **Unified Endpoint** — Eén enkele `http://localhost:20128/v1` dient als proxy voor alle 67+ providers
|
||||
- **Formatvertaling** — Automatisch en transparant: OpenAI ↔ Claude ↔ Gemini ↔ Responses API
|
||||
- **Response Sanitization** — Verwijdert niet-standaardvelden (`x_groq`, `usage_breakdown`, `service_tier`) die OpenAI SDK v1.83+ breken
|
||||
- **Rolnormalisatie** — Converteert `developer` → `system` voor niet-OpenAI-providers; `system` → `user` voor GLM/ERNIE
|
||||
|
||||
@@ -11,28 +11,6 @@ _Din universelle API-proxy – ett endepunkt, 36+ leverandører, null nedetid._
|
||||
|
||||
---
|
||||
|
||||
### 🆕 What's New in v2.7.0
|
||||
|
||||
- **Pluggable RouterStrategy** — rules, cost, and latency routing strategies
|
||||
- **Multilingual intent detection** — routing scoring in 30+ languages
|
||||
- **Request deduplication** — prevent duplicate API calls via content hash
|
||||
- **New providers:** Grok-4 Fast (xAI), GLM-5 / Z.AI, MiniMax M2.5, Kimi K2.5
|
||||
- **Updated pricing:** Grok-4 Fast $0.20/$0.50/M, GLM-5 $0.50/M, MiniMax M2.5 $0.30/M
|
||||
|
||||
---
|
||||
|
||||
### 🚀 New in v2.0.9+ — Playground, CLI Fingerprints & ACP
|
||||
|
||||
| Feature | What It Does |
|
||||
| ------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| 🎮 **Model Playground** | Dashboard page to test any model directly — provider/model/endpoint selectors, Monaco Editor, streaming, abort, timing |
|
||||
| 🔏 **CLI Fingerprint Matching** | Per-provider header/body ordering to match native CLI signatures — toggle per provider in Settings > Security. **Your proxy IP is preserved** |
|
||||
| 🤝 **ACP Support (Agent Client Protocol)** | CLI agent discovery (Codex, Claude, Goose, Gemini CLI, OpenClaw), process spawner, `/api/acp/agents` endpoint |
|
||||
| 🤖 **ACP Agents Dashboard** | Debug > Agents page — grid of 14 agents with install status, version, custom agent form for any CLI tool |
|
||||
| 🔧 **Custom Model `apiFormat` Routing** | Custom models with `apiFormat: "responses"` now correctly route to the Responses API translator |
|
||||
| 🏢 **Codex Workspace Isolation** | Multiple Codex workspaces per email — OAuth correctly separates connections by workspace ID |
|
||||
| 🔄 **Electron Auto-Update** | Desktop app checks for updates + auto-install on restart |
|
||||
|
||||
### 🤖 Gratis AI-leverandør for dine favorittkodeagenter
|
||||
|
||||
_Koble til ethvert AI-drevet IDE- eller CLI-verktøy gjennom OmniRoute – gratis API-gateway for ubegrenset koding._
|
||||
@@ -118,6 +96,38 @@ _Koble til ethvert AI-drevet IDE- eller CLI-verktøy gjennom OmniRoute – grati
|
||||
|
||||
---
|
||||
|
||||
### 🆕 What's New in v3.0.0
|
||||
|
||||
| Area | Change |
|
||||
| --- | --- |
|
||||
| 🔒 **CodeQL Security** | Fixed 10+ CodeQL alerts: polynomial-redos, insecure-randomness, shell-injection |
|
||||
| ✅ **Route Validation** | All 176 API routes validated with Zod schemas + `validateBody()` |
|
||||
| 🐛 **omniModel Tag Leak** | Internal `<omniModel>` tags no longer leak to clients in SSE streams (#585) |
|
||||
| 🔑 **Registered Keys API** | Auto-provision API keys via `POST /api/v1/registered-keys` with quota enforcement |
|
||||
| 🎨 **Provider Icons** | 130+ provider logos via `@lobehub/icons` (SVG) with PNG fallback |
|
||||
| 🔄 **Model Auto-Sync** | 24h scheduler refreshes model lists for 16 providers |
|
||||
| 🌐 **OpenCode Zen/Go** | Two new providers: free tier + subscription tier |
|
||||
| 🔧 **926 Tests** | Full test suite passes with 0 failures |
|
||||
|
||||
|
||||
|
||||
### 🆕 What's New in v3.0.0
|
||||
|
||||
| Area | Change |
|
||||
| --- | --- |
|
||||
| 🔒 **CodeQL Security** | Fixed 10+ CodeQL alerts: polynomial-redos, insecure-randomness, shell-injection |
|
||||
| ✅ **Route Validation** | All 176 API routes validated with Zod schemas + `validateBody()` |
|
||||
| 🐛 **omniModel Tag Leak** | Internal `<omniModel>` tags no longer leak to clients in SSE streams (#585) |
|
||||
| 🔑 **Registered Keys API** | Auto-provision API keys via `POST /api/v1/registered-keys` with quota enforcement |
|
||||
| 🎨 **Provider Icons** | 130+ provider logos via `@lobehub/icons` (SVG) with PNG fallback |
|
||||
| 🔄 **Model Auto-Sync** | 24h scheduler refreshes model lists for 16 providers |
|
||||
| 🌐 **OpenCode Zen/Go** | Two new providers: free tier + subscription tier |
|
||||
| 🔧 **926 Tests** | Full test suite passes with 0 failures |
|
||||
|
||||
---
|
||||
|
||||
|
||||
|
||||
[](https://www.npmjs.com/package/omniroute)
|
||||
[](https://hub.docker.com/r/diegosouzapw/omniroute)
|
||||
[](https://github.com/diegosouzapw/OmniRoute/blob/main/LICENSE)
|
||||
|
||||
@@ -5,34 +5,12 @@
|
||||
|
||||
### Huwag kailanman ihinto ang coding. Smart routing sa **LIBRE at murang mga modelo ng AI** na may awtomatikong fallback.
|
||||
|
||||
_Iyong unibersal na API proxy — isang endpoint, 36+ provider, zero downtime._
|
||||
_Iyong unibersal na API proxy — isang endpoint, 67+ provider, zero downtime._
|
||||
|
||||
**Mga Pagkumpleto ng Chat • Mga Pag-embed • Pagbuo ng Imahe • Audio • Pag-rerank • 100% TypeScript**
|
||||
|
||||
---
|
||||
|
||||
### 🆕 What's New in v2.7.0
|
||||
|
||||
- **Pluggable RouterStrategy** — rules, cost, and latency routing strategies
|
||||
- **Multilingual intent detection** — routing scoring in 30+ languages
|
||||
- **Request deduplication** — prevent duplicate API calls via content hash
|
||||
- **New providers:** Grok-4 Fast (xAI), GLM-5 / Z.AI, MiniMax M2.5, Kimi K2.5
|
||||
- **Updated pricing:** Grok-4 Fast $0.20/$0.50/M, GLM-5 $0.50/M, MiniMax M2.5 $0.30/M
|
||||
|
||||
---
|
||||
|
||||
### 🚀 New in v2.0.9+ — Playground, CLI Fingerprints & ACP
|
||||
|
||||
| Feature | What It Does |
|
||||
| ------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| 🎮 **Model Playground** | Dashboard page to test any model directly — provider/model/endpoint selectors, Monaco Editor, streaming, abort, timing |
|
||||
| 🔏 **CLI Fingerprint Matching** | Per-provider header/body ordering to match native CLI signatures — toggle per provider in Settings > Security. **Your proxy IP is preserved** |
|
||||
| 🤝 **ACP Support (Agent Client Protocol)** | CLI agent discovery (Codex, Claude, Goose, Gemini CLI, OpenClaw), process spawner, `/api/acp/agents` endpoint |
|
||||
| 🤖 **ACP Agents Dashboard** | Debug > Agents page — grid of 14 agents with install status, version, custom agent form for any CLI tool |
|
||||
| 🔧 **Custom Model `apiFormat` Routing** | Custom models with `apiFormat: "responses"` now correctly route to the Responses API translator |
|
||||
| 🏢 **Codex Workspace Isolation** | Multiple Codex workspaces per email — OAuth correctly separates connections by workspace ID |
|
||||
| 🔄 **Electron Auto-Update** | Desktop app checks for updates + auto-install on restart |
|
||||
|
||||
### 🤖 Libreng AI Provider para sa iyong mga paboritong coding agent
|
||||
|
||||
_Ikonekta ang anumang AI-powered IDE o CLI tool sa pamamagitan ng OmniRoute — libreng API gateway para sa walang limitasyong coding._
|
||||
@@ -118,6 +96,38 @@ _Ikonekta ang anumang AI-powered IDE o CLI tool sa pamamagitan ng OmniRoute —
|
||||
|
||||
---
|
||||
|
||||
### 🆕 What's New in v3.0.0
|
||||
|
||||
| Area | Change |
|
||||
| --- | --- |
|
||||
| 🔒 **CodeQL Security** | Fixed 10+ CodeQL alerts: polynomial-redos, insecure-randomness, shell-injection |
|
||||
| ✅ **Route Validation** | All 176 API routes validated with Zod schemas + `validateBody()` |
|
||||
| 🐛 **omniModel Tag Leak** | Internal `<omniModel>` tags no longer leak to clients in SSE streams (#585) |
|
||||
| 🔑 **Registered Keys API** | Auto-provision API keys via `POST /api/v1/registered-keys` with quota enforcement |
|
||||
| 🎨 **Provider Icons** | 130+ provider logos via `@lobehub/icons` (SVG) with PNG fallback |
|
||||
| 🔄 **Model Auto-Sync** | 24h scheduler refreshes model lists for 16 providers |
|
||||
| 🌐 **OpenCode Zen/Go** | Two new providers: free tier + subscription tier |
|
||||
| 🔧 **926 Tests** | Full test suite passes with 0 failures |
|
||||
|
||||
|
||||
|
||||
### 🆕 What's New in v3.0.0
|
||||
|
||||
| Area | Change |
|
||||
| --- | --- |
|
||||
| 🔒 **CodeQL Security** | Fixed 10+ CodeQL alerts: polynomial-redos, insecure-randomness, shell-injection |
|
||||
| ✅ **Route Validation** | All 176 API routes validated with Zod schemas + `validateBody()` |
|
||||
| 🐛 **omniModel Tag Leak** | Internal `<omniModel>` tags no longer leak to clients in SSE streams (#585) |
|
||||
| 🔑 **Registered Keys API** | Auto-provision API keys via `POST /api/v1/registered-keys` with quota enforcement |
|
||||
| 🎨 **Provider Icons** | 130+ provider logos via `@lobehub/icons` (SVG) with PNG fallback |
|
||||
| 🔄 **Model Auto-Sync** | 24h scheduler refreshes model lists for 16 providers |
|
||||
| 🌐 **OpenCode Zen/Go** | Two new providers: free tier + subscription tier |
|
||||
| 🔧 **926 Tests** | Full test suite passes with 0 failures |
|
||||
|
||||
---
|
||||
|
||||
|
||||
|
||||
[](https://www.npmjs.com/package/omniroute)
|
||||
[](https://hub.docker.com/r/diegosouzapw/omniroute)
|
||||
[](https://github.com/diegosouzapw/OmniRoute/blob/main/LICENSE)
|
||||
@@ -246,7 +256,7 @@ Gumagamit ang OpenAI ng isang format, gumagamit si Claude (Anthropic) ng isa pa,
|
||||
|
||||
**Paano ito niresolba ng OmniRoute:**
|
||||
|
||||
- **Pinag-isang Endpoint** — Isang `http://localhost:20128/v1` ang nagsisilbing proxy para sa lahat ng 36+ provider
|
||||
- **Pinag-isang Endpoint** — Isang `http://localhost:20128/v1` ang nagsisilbing proxy para sa lahat ng 67+ provider
|
||||
- **Format Translation** — Awtomatiko at transparent: OpenAI ↔ Claude ↔ Gemini ↔ Responses API
|
||||
- **Response Sanitization** — Tinatanggal ang mga hindi karaniwang field (`x_groq`, `usage_breakdown`, `service_tier`) na sumisira sa OpenAI SDK v1.83+
|
||||
- **Role Normalization** — Kino-convert ang `developer` → `system` para sa mga provider na hindi OpenAI; `system` → `user` para sa GLM/ERNIE
|
||||
@@ -331,7 +341,7 @@ Gumagamit ang mga developer ng Cursor, Claude Code, Codex CLI, OpenClaw, Gemini
|
||||
- **CLI Tools Dashboard** — Nakatuon na page na may isang-click na setup para sa Claude Code, Codex CLI, OpenClaw, Kilo Code, Antigravity, Cline
|
||||
- **GitHub Copilot Config Generator** — Bumubuo ng `chatLanguageModels.json` para sa VS Code na may maramihang pagpili ng modelo
|
||||
- **Onboarding Wizard** — May gabay na 4-step na pag-setup para sa mga unang beses na user
|
||||
- **Isang endpoint, lahat ng modelo** — I-configure ang `http://localhost:20128/v1` nang isang beses, i-access ang 36+ provider
|
||||
- **Isang endpoint, lahat ng modelo** — I-configure ang `http://localhost:20128/v1` nang isang beses, i-access ang 67+ provider
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
@@ -11,28 +11,6 @@ _Twój uniwersalny serwer proxy API — jeden punkt końcowy, ponad 36 dostawcó
|
||||
|
||||
---
|
||||
|
||||
### 🆕 What's New in v2.7.0
|
||||
|
||||
- **Pluggable RouterStrategy** — rules, cost, and latency routing strategies
|
||||
- **Multilingual intent detection** — routing scoring in 30+ languages
|
||||
- **Request deduplication** — prevent duplicate API calls via content hash
|
||||
- **New providers:** Grok-4 Fast (xAI), GLM-5 / Z.AI, MiniMax M2.5, Kimi K2.5
|
||||
- **Updated pricing:** Grok-4 Fast $0.20/$0.50/M, GLM-5 $0.50/M, MiniMax M2.5 $0.30/M
|
||||
|
||||
---
|
||||
|
||||
### 🚀 New in v2.0.9+ — Playground, CLI Fingerprints & ACP
|
||||
|
||||
| Feature | What It Does |
|
||||
| ------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| 🎮 **Model Playground** | Dashboard page to test any model directly — provider/model/endpoint selectors, Monaco Editor, streaming, abort, timing |
|
||||
| 🔏 **CLI Fingerprint Matching** | Per-provider header/body ordering to match native CLI signatures — toggle per provider in Settings > Security. **Your proxy IP is preserved** |
|
||||
| 🤝 **ACP Support (Agent Client Protocol)** | CLI agent discovery (Codex, Claude, Goose, Gemini CLI, OpenClaw), process spawner, `/api/acp/agents` endpoint |
|
||||
| 🤖 **ACP Agents Dashboard** | Debug > Agents page — grid of 14 agents with install status, version, custom agent form for any CLI tool |
|
||||
| 🔧 **Custom Model `apiFormat` Routing** | Custom models with `apiFormat: "responses"` now correctly route to the Responses API translator |
|
||||
| 🏢 **Codex Workspace Isolation** | Multiple Codex workspaces per email — OAuth correctly separates connections by workspace ID |
|
||||
| 🔄 **Electron Auto-Update** | Desktop app checks for updates + auto-install on restart |
|
||||
|
||||
### 🤖 Bezpłatny dostawca AI dla Twoich ulubionych agentów kodujących
|
||||
|
||||
_Połącz dowolne narzędzie IDE lub CLI oparte na sztucznej inteligencji poprzez OmniRoute — bezpłatną bramę API dla nieograniczonego kodowania._
|
||||
@@ -118,6 +96,38 @@ _Połącz dowolne narzędzie IDE lub CLI oparte na sztucznej inteligencji poprze
|
||||
|
||||
---
|
||||
|
||||
### 🆕 What's New in v3.0.0
|
||||
|
||||
| Area | Change |
|
||||
| --- | --- |
|
||||
| 🔒 **CodeQL Security** | Fixed 10+ CodeQL alerts: polynomial-redos, insecure-randomness, shell-injection |
|
||||
| ✅ **Route Validation** | All 176 API routes validated with Zod schemas + `validateBody()` |
|
||||
| 🐛 **omniModel Tag Leak** | Internal `<omniModel>` tags no longer leak to clients in SSE streams (#585) |
|
||||
| 🔑 **Registered Keys API** | Auto-provision API keys via `POST /api/v1/registered-keys` with quota enforcement |
|
||||
| 🎨 **Provider Icons** | 130+ provider logos via `@lobehub/icons` (SVG) with PNG fallback |
|
||||
| 🔄 **Model Auto-Sync** | 24h scheduler refreshes model lists for 16 providers |
|
||||
| 🌐 **OpenCode Zen/Go** | Two new providers: free tier + subscription tier |
|
||||
| 🔧 **926 Tests** | Full test suite passes with 0 failures |
|
||||
|
||||
|
||||
|
||||
### 🆕 What's New in v3.0.0
|
||||
|
||||
| Area | Change |
|
||||
| --- | --- |
|
||||
| 🔒 **CodeQL Security** | Fixed 10+ CodeQL alerts: polynomial-redos, insecure-randomness, shell-injection |
|
||||
| ✅ **Route Validation** | All 176 API routes validated with Zod schemas + `validateBody()` |
|
||||
| 🐛 **omniModel Tag Leak** | Internal `<omniModel>` tags no longer leak to clients in SSE streams (#585) |
|
||||
| 🔑 **Registered Keys API** | Auto-provision API keys via `POST /api/v1/registered-keys` with quota enforcement |
|
||||
| 🎨 **Provider Icons** | 130+ provider logos via `@lobehub/icons` (SVG) with PNG fallback |
|
||||
| 🔄 **Model Auto-Sync** | 24h scheduler refreshes model lists for 16 providers |
|
||||
| 🌐 **OpenCode Zen/Go** | Two new providers: free tier + subscription tier |
|
||||
| 🔧 **926 Tests** | Full test suite passes with 0 failures |
|
||||
|
||||
---
|
||||
|
||||
|
||||
|
||||
[](https://www.npmjs.com/package/omniroute)
|
||||
[](https://hub.docker.com/r/diegosouzapw/omniroute)
|
||||
[](https://github.com/diegosouzapw/OmniRoute/blob/main/LICENSE)
|
||||
|
||||
@@ -11,28 +11,6 @@ _Seu proxy de API universal — um endpoint, 36+ provedores, zero tempo de inati
|
||||
|
||||
---
|
||||
|
||||
### 🆕 Novidades na v2.7.0
|
||||
|
||||
- **RouterStrategy plugável** — estratégias de regras, custo e latência
|
||||
- **Detecção de intenção multilíngue** — scoring de roteamento em 30+ idiomas
|
||||
- **Deduplicação de requisições** — evita chamadas duplicadas por hash de conteúdo
|
||||
- **Novos provedores:** Grok-4 Fast (xAI), GLM-5 / Z.AI, MiniMax M2.5, Kimi K2.5
|
||||
- **Preços atualizados:** Grok-4 Fast $0.20/$0.50/M, GLM-5 $0.50/M, MiniMax M2.5 $0.30/M
|
||||
|
||||
---
|
||||
|
||||
### 🚀 New in v2.0.9+ — Playground, CLI Fingerprints & ACP
|
||||
|
||||
| Feature | What It Does |
|
||||
| ------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| 🎮 **Model Playground** | Dashboard page to test any model directly — provider/model/endpoint selectors, Monaco Editor, streaming, abort, timing |
|
||||
| 🔏 **CLI Fingerprint Matching** | Per-provider header/body ordering to match native CLI signatures — toggle per provider in Settings > Security. **Your proxy IP is preserved** |
|
||||
| 🤝 **ACP Support (Agent Client Protocol)** | CLI agent discovery (Codex, Claude, Goose, Gemini CLI, OpenClaw), process spawner, `/api/acp/agents` endpoint |
|
||||
| 🤖 **ACP Agents Dashboard** | Debug > Agents page — grid of 14 agents with install status, version, custom agent form for any CLI tool |
|
||||
| 🔧 **Custom Model `apiFormat` Routing** | Custom models with `apiFormat: "responses"` now correctly route to the Responses API translator |
|
||||
| 🏢 **Codex Workspace Isolation** | Multiple Codex workspaces per email — OAuth correctly separates connections by workspace ID |
|
||||
| 🔄 **Electron Auto-Update** | Desktop app checks for updates + auto-install on restart |
|
||||
|
||||
### 🤖 Provedor de IA Gratuito para seus agentes de programação favoritos
|
||||
|
||||
_Conecte qualquer IDE ou ferramenta CLI com IA através do OmniRoute — gateway de API gratuito para programação ilimitada._
|
||||
@@ -118,6 +96,38 @@ _Conecte qualquer IDE ou ferramenta CLI com IA através do OmniRoute — gateway
|
||||
|
||||
---
|
||||
|
||||
### 🆕 What's New in v3.0.0
|
||||
|
||||
| Area | Change |
|
||||
| --- | --- |
|
||||
| 🔒 **CodeQL Security** | Fixed 10+ CodeQL alerts: polynomial-redos, insecure-randomness, shell-injection |
|
||||
| ✅ **Route Validation** | All 176 API routes validated with Zod schemas + `validateBody()` |
|
||||
| 🐛 **omniModel Tag Leak** | Internal `<omniModel>` tags no longer leak to clients in SSE streams (#585) |
|
||||
| 🔑 **Registered Keys API** | Auto-provision API keys via `POST /api/v1/registered-keys` with quota enforcement |
|
||||
| 🎨 **Provider Icons** | 130+ provider logos via `@lobehub/icons` (SVG) with PNG fallback |
|
||||
| 🔄 **Model Auto-Sync** | 24h scheduler refreshes model lists for 16 providers |
|
||||
| 🌐 **OpenCode Zen/Go** | Two new providers: free tier + subscription tier |
|
||||
| 🔧 **926 Tests** | Full test suite passes with 0 failures |
|
||||
|
||||
|
||||
|
||||
### 🆕 What's New in v3.0.0
|
||||
|
||||
| Area | Change |
|
||||
| --- | --- |
|
||||
| 🔒 **CodeQL Security** | Fixed 10+ CodeQL alerts: polynomial-redos, insecure-randomness, shell-injection |
|
||||
| ✅ **Route Validation** | All 176 API routes validated with Zod schemas + `validateBody()` |
|
||||
| 🐛 **omniModel Tag Leak** | Internal `<omniModel>` tags no longer leak to clients in SSE streams (#585) |
|
||||
| 🔑 **Registered Keys API** | Auto-provision API keys via `POST /api/v1/registered-keys` with quota enforcement |
|
||||
| 🎨 **Provider Icons** | 130+ provider logos via `@lobehub/icons` (SVG) with PNG fallback |
|
||||
| 🔄 **Model Auto-Sync** | 24h scheduler refreshes model lists for 16 providers |
|
||||
| 🌐 **OpenCode Zen/Go** | Two new providers: free tier + subscription tier |
|
||||
| 🔧 **926 Tests** | Full test suite passes with 0 failures |
|
||||
|
||||
---
|
||||
|
||||
|
||||
|
||||
[](https://www.npmjs.com/package/omniroute)
|
||||
[](https://hub.docker.com/r/diegosouzapw/omniroute)
|
||||
[](https://github.com/diegosouzapw/OmniRoute/blob/main/LICENSE)
|
||||
|
||||
@@ -11,28 +11,6 @@ _Seu proxy de API universal — um endpoint, mais de 36 provedores, tempo de ina
|
||||
|
||||
---
|
||||
|
||||
### 🆕 Novidades na v2.7.0
|
||||
|
||||
- **RouterStrategy extensível** — estratégias de regras, custo e latência
|
||||
- **Deteção de intenção multilíngue** — scoring de encaminhamento em 30+ idiomas
|
||||
- **Deduplicação de pedidos** — evita chamadas duplicadas por hash de conteúdo
|
||||
- **Novos fornecedores:** Grok-4 Fast (xAI), GLM-5 / Z.AI, MiniMax M2.5, Kimi K2.5
|
||||
- **Preços atualizados:** Grok-4 Fast $0.20/$0.50/M, GLM-5 $0.50/M, MiniMax M2.5 $0.30/M
|
||||
|
||||
---
|
||||
|
||||
### 🚀 New in v2.0.9+ — Playground, CLI Fingerprints & ACP
|
||||
|
||||
| Feature | What It Does |
|
||||
| ------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| 🎮 **Model Playground** | Dashboard page to test any model directly — provider/model/endpoint selectors, Monaco Editor, streaming, abort, timing |
|
||||
| 🔏 **CLI Fingerprint Matching** | Per-provider header/body ordering to match native CLI signatures — toggle per provider in Settings > Security. **Your proxy IP is preserved** |
|
||||
| 🤝 **ACP Support (Agent Client Protocol)** | CLI agent discovery (Codex, Claude, Goose, Gemini CLI, OpenClaw), process spawner, `/api/acp/agents` endpoint |
|
||||
| 🤖 **ACP Agents Dashboard** | Debug > Agents page — grid of 14 agents with install status, version, custom agent form for any CLI tool |
|
||||
| 🔧 **Custom Model `apiFormat` Routing** | Custom models with `apiFormat: "responses"` now correctly route to the Responses API translator |
|
||||
| 🏢 **Codex Workspace Isolation** | Multiple Codex workspaces per email — OAuth correctly separates connections by workspace ID |
|
||||
| 🔄 **Electron Auto-Update** | Desktop app checks for updates + auto-install on restart |
|
||||
|
||||
### 🤖 Provedor de IA gratuito para seus agentes de codificação favoritos
|
||||
|
||||
_Conecte qualquer ferramenta IDE ou CLI com tecnologia de IA por meio do OmniRoute - gateway de API gratuito para codificação ilimitada._
|
||||
@@ -118,6 +96,38 @@ _Conecte qualquer ferramenta IDE ou CLI com tecnologia de IA por meio do OmniRou
|
||||
|
||||
---
|
||||
|
||||
### 🆕 What's New in v3.0.0
|
||||
|
||||
| Area | Change |
|
||||
| --- | --- |
|
||||
| 🔒 **CodeQL Security** | Fixed 10+ CodeQL alerts: polynomial-redos, insecure-randomness, shell-injection |
|
||||
| ✅ **Route Validation** | All 176 API routes validated with Zod schemas + `validateBody()` |
|
||||
| 🐛 **omniModel Tag Leak** | Internal `<omniModel>` tags no longer leak to clients in SSE streams (#585) |
|
||||
| 🔑 **Registered Keys API** | Auto-provision API keys via `POST /api/v1/registered-keys` with quota enforcement |
|
||||
| 🎨 **Provider Icons** | 130+ provider logos via `@lobehub/icons` (SVG) with PNG fallback |
|
||||
| 🔄 **Model Auto-Sync** | 24h scheduler refreshes model lists for 16 providers |
|
||||
| 🌐 **OpenCode Zen/Go** | Two new providers: free tier + subscription tier |
|
||||
| 🔧 **926 Tests** | Full test suite passes with 0 failures |
|
||||
|
||||
|
||||
|
||||
### 🆕 What's New in v3.0.0
|
||||
|
||||
| Area | Change |
|
||||
| --- | --- |
|
||||
| 🔒 **CodeQL Security** | Fixed 10+ CodeQL alerts: polynomial-redos, insecure-randomness, shell-injection |
|
||||
| ✅ **Route Validation** | All 176 API routes validated with Zod schemas + `validateBody()` |
|
||||
| 🐛 **omniModel Tag Leak** | Internal `<omniModel>` tags no longer leak to clients in SSE streams (#585) |
|
||||
| 🔑 **Registered Keys API** | Auto-provision API keys via `POST /api/v1/registered-keys` with quota enforcement |
|
||||
| 🎨 **Provider Icons** | 130+ provider logos via `@lobehub/icons` (SVG) with PNG fallback |
|
||||
| 🔄 **Model Auto-Sync** | 24h scheduler refreshes model lists for 16 providers |
|
||||
| 🌐 **OpenCode Zen/Go** | Two new providers: free tier + subscription tier |
|
||||
| 🔧 **926 Tests** | Full test suite passes with 0 failures |
|
||||
|
||||
---
|
||||
|
||||
|
||||
|
||||
[](https://www.npmjs.com/package/omniroute)
|
||||
[](https://hub.docker.com/r/diegosouzapw/omniroute)
|
||||
[](https://github.com/diegosouzapw/OmniRoute/blob/main/LICENSE)
|
||||
|
||||
@@ -11,28 +11,6 @@ _Proxy-ul dvs. universal API - un punct final, peste 36 de furnizori, zero timpi
|
||||
|
||||
---
|
||||
|
||||
### 🆕 What's New in v2.7.0
|
||||
|
||||
- **Pluggable RouterStrategy** — rules, cost, and latency routing strategies
|
||||
- **Multilingual intent detection** — routing scoring in 30+ languages
|
||||
- **Request deduplication** — prevent duplicate API calls via content hash
|
||||
- **New providers:** Grok-4 Fast (xAI), GLM-5 / Z.AI, MiniMax M2.5, Kimi K2.5
|
||||
- **Updated pricing:** Grok-4 Fast $0.20/$0.50/M, GLM-5 $0.50/M, MiniMax M2.5 $0.30/M
|
||||
|
||||
---
|
||||
|
||||
### 🚀 New in v2.0.9+ — Playground, CLI Fingerprints & ACP
|
||||
|
||||
| Feature | What It Does |
|
||||
| ------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| 🎮 **Model Playground** | Dashboard page to test any model directly — provider/model/endpoint selectors, Monaco Editor, streaming, abort, timing |
|
||||
| 🔏 **CLI Fingerprint Matching** | Per-provider header/body ordering to match native CLI signatures — toggle per provider in Settings > Security. **Your proxy IP is preserved** |
|
||||
| 🤝 **ACP Support (Agent Client Protocol)** | CLI agent discovery (Codex, Claude, Goose, Gemini CLI, OpenClaw), process spawner, `/api/acp/agents` endpoint |
|
||||
| 🤖 **ACP Agents Dashboard** | Debug > Agents page — grid of 14 agents with install status, version, custom agent form for any CLI tool |
|
||||
| 🔧 **Custom Model `apiFormat` Routing** | Custom models with `apiFormat: "responses"` now correctly route to the Responses API translator |
|
||||
| 🏢 **Codex Workspace Isolation** | Multiple Codex workspaces per email — OAuth correctly separates connections by workspace ID |
|
||||
| 🔄 **Electron Auto-Update** | Desktop app checks for updates + auto-install on restart |
|
||||
|
||||
### 🤖 Furnizor AI gratuit pentru agenții tăi preferați de codare
|
||||
|
||||
_Conectați orice instrument IDE sau CLI alimentat de AI prin OmniRoute — gateway API gratuit pentru codare nelimitată._
|
||||
@@ -118,6 +96,38 @@ _Conectați orice instrument IDE sau CLI alimentat de AI prin OmniRoute — gate
|
||||
|
||||
---
|
||||
|
||||
### 🆕 What's New in v3.0.0
|
||||
|
||||
| Area | Change |
|
||||
| --- | --- |
|
||||
| 🔒 **CodeQL Security** | Fixed 10+ CodeQL alerts: polynomial-redos, insecure-randomness, shell-injection |
|
||||
| ✅ **Route Validation** | All 176 API routes validated with Zod schemas + `validateBody()` |
|
||||
| 🐛 **omniModel Tag Leak** | Internal `<omniModel>` tags no longer leak to clients in SSE streams (#585) |
|
||||
| 🔑 **Registered Keys API** | Auto-provision API keys via `POST /api/v1/registered-keys` with quota enforcement |
|
||||
| 🎨 **Provider Icons** | 130+ provider logos via `@lobehub/icons` (SVG) with PNG fallback |
|
||||
| 🔄 **Model Auto-Sync** | 24h scheduler refreshes model lists for 16 providers |
|
||||
| 🌐 **OpenCode Zen/Go** | Two new providers: free tier + subscription tier |
|
||||
| 🔧 **926 Tests** | Full test suite passes with 0 failures |
|
||||
|
||||
|
||||
|
||||
### 🆕 What's New in v3.0.0
|
||||
|
||||
| Area | Change |
|
||||
| --- | --- |
|
||||
| 🔒 **CodeQL Security** | Fixed 10+ CodeQL alerts: polynomial-redos, insecure-randomness, shell-injection |
|
||||
| ✅ **Route Validation** | All 176 API routes validated with Zod schemas + `validateBody()` |
|
||||
| 🐛 **omniModel Tag Leak** | Internal `<omniModel>` tags no longer leak to clients in SSE streams (#585) |
|
||||
| 🔑 **Registered Keys API** | Auto-provision API keys via `POST /api/v1/registered-keys` with quota enforcement |
|
||||
| 🎨 **Provider Icons** | 130+ provider logos via `@lobehub/icons` (SVG) with PNG fallback |
|
||||
| 🔄 **Model Auto-Sync** | 24h scheduler refreshes model lists for 16 providers |
|
||||
| 🌐 **OpenCode Zen/Go** | Two new providers: free tier + subscription tier |
|
||||
| 🔧 **926 Tests** | Full test suite passes with 0 failures |
|
||||
|
||||
---
|
||||
|
||||
|
||||
|
||||
[](https://www.npmjs.com/package/omniroute)
|
||||
[](https://hub.docker.com/r/diegosouzapw/omniroute)
|
||||
[](https://github.com/diegosouzapw/OmniRoute/blob/main/LICENSE)
|
||||
|
||||
@@ -11,28 +11,6 @@ _Ваш универсальный API-прокси — одна точка до
|
||||
|
||||
---
|
||||
|
||||
### 🆕 Новое в v2.7.0
|
||||
|
||||
- **Подключаемая RouterStrategy** — стратегии по правилам, стоимости и задержке
|
||||
- **Многоязычное распознавание намерений** — маршрутизация на 30+ языках
|
||||
- **Дедупликация запросов** — устранение дублей по хэшу содержимого
|
||||
- **Новые провайдеры:** Grok-4 Fast (xAI), GLM-5 / Z.AI, MiniMax M2.5, Kimi K2.5
|
||||
- **Обновлённые цены:** Grok-4 Fast $0.20/$0.50/M, GLM-5 $0.50/M, MiniMax M2.5 $0.30/M
|
||||
|
||||
---
|
||||
|
||||
### 🚀 New in v2.0.9+ — Playground, CLI Fingerprints & ACP
|
||||
|
||||
| Feature | What It Does |
|
||||
| ------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| 🎮 **Model Playground** | Dashboard page to test any model directly — provider/model/endpoint selectors, Monaco Editor, streaming, abort, timing |
|
||||
| 🔏 **CLI Fingerprint Matching** | Per-provider header/body ordering to match native CLI signatures — toggle per provider in Settings > Security. **Your proxy IP is preserved** |
|
||||
| 🤝 **ACP Support (Agent Client Protocol)** | CLI agent discovery (Codex, Claude, Goose, Gemini CLI, OpenClaw), process spawner, `/api/acp/agents` endpoint |
|
||||
| 🤖 **ACP Agents Dashboard** | Debug > Agents page — grid of 14 agents with install status, version, custom agent form for any CLI tool |
|
||||
| 🔧 **Custom Model `apiFormat` Routing** | Custom models with `apiFormat: "responses"` now correctly route to the Responses API translator |
|
||||
| 🏢 **Codex Workspace Isolation** | Multiple Codex workspaces per email — OAuth correctly separates connections by workspace ID |
|
||||
| 🔄 **Electron Auto-Update** | Desktop app checks for updates + auto-install on restart |
|
||||
|
||||
### 🤖 Бесплатный AI-провайдер для ваших любимых агентов программирования
|
||||
|
||||
_Подключайте любую IDE или CLI-инструмент с AI через OmniRoute — бесплатный API gateway для неограниченного программирования._
|
||||
@@ -118,6 +96,38 @@ _Подключайте любую IDE или CLI-инструмент с AI ч
|
||||
|
||||
---
|
||||
|
||||
### 🆕 What's New in v3.0.0
|
||||
|
||||
| Area | Change |
|
||||
| --- | --- |
|
||||
| 🔒 **CodeQL Security** | Fixed 10+ CodeQL alerts: polynomial-redos, insecure-randomness, shell-injection |
|
||||
| ✅ **Route Validation** | All 176 API routes validated with Zod schemas + `validateBody()` |
|
||||
| 🐛 **omniModel Tag Leak** | Internal `<omniModel>` tags no longer leak to clients in SSE streams (#585) |
|
||||
| 🔑 **Registered Keys API** | Auto-provision API keys via `POST /api/v1/registered-keys` with quota enforcement |
|
||||
| 🎨 **Provider Icons** | 130+ provider logos via `@lobehub/icons` (SVG) with PNG fallback |
|
||||
| 🔄 **Model Auto-Sync** | 24h scheduler refreshes model lists for 16 providers |
|
||||
| 🌐 **OpenCode Zen/Go** | Two new providers: free tier + subscription tier |
|
||||
| 🔧 **926 Tests** | Full test suite passes with 0 failures |
|
||||
|
||||
|
||||
|
||||
### 🆕 What's New in v3.0.0
|
||||
|
||||
| Area | Change |
|
||||
| --- | --- |
|
||||
| 🔒 **CodeQL Security** | Fixed 10+ CodeQL alerts: polynomial-redos, insecure-randomness, shell-injection |
|
||||
| ✅ **Route Validation** | All 176 API routes validated with Zod schemas + `validateBody()` |
|
||||
| 🐛 **omniModel Tag Leak** | Internal `<omniModel>` tags no longer leak to clients in SSE streams (#585) |
|
||||
| 🔑 **Registered Keys API** | Auto-provision API keys via `POST /api/v1/registered-keys` with quota enforcement |
|
||||
| 🎨 **Provider Icons** | 130+ provider logos via `@lobehub/icons` (SVG) with PNG fallback |
|
||||
| 🔄 **Model Auto-Sync** | 24h scheduler refreshes model lists for 16 providers |
|
||||
| 🌐 **OpenCode Zen/Go** | Two new providers: free tier + subscription tier |
|
||||
| 🔧 **926 Tests** | Full test suite passes with 0 failures |
|
||||
|
||||
---
|
||||
|
||||
|
||||
|
||||
[](https://www.npmjs.com/package/omniroute)
|
||||
[](https://hub.docker.com/r/diegosouzapw/omniroute)
|
||||
[](https://github.com/diegosouzapw/OmniRoute/blob/main/LICENSE)
|
||||
@@ -376,7 +386,7 @@ Claude Code, Codex, Gemini CLI, Copilot — все используют OAuth 2.
|
||||
- **Панель управления унифицированными журналами** — 4 вкладки: журналы запросов, журналы прокси, журналы аудита, консоль.
|
||||
- **Консольный просмотр журнала** — просмотрщик в режиме терминала в режиме реального времени с уровнями с цветовой кодировкой, автоматической прокруткой, поиском и фильтрацией.
|
||||
- **Журналы прокси-сервера SQLite** — постоянные журналы, сохраняющиеся после перезапуска сервера.
|
||||
- **Площадка переводчика** — 4 режима отладки: Площадка (перевод формата), Тестер чата (туда и обратно), Тестовый стенд (пакетный), Мониторинг в реальном времени (в режиме реального времени).
|
||||
- **Площадка транслятора (Translator Playground)** — 4 режима отладки: Площадка (перевод формата), Тестер чата (туда и обратно), Тестовый стенд (пакетный), Мониторинг в реальном времени (в режиме реального времени).
|
||||
- **Запрос телеметрии** — задержка p50/p95/p99 + отслеживание X-Request-Id
|
||||
- **Журналирование на основе файлов с ротацией** — перехватчик консоли записывает все в журнал JSON с ротацией на основе размера.
|
||||
|
||||
@@ -441,7 +451,7 @@ Claude Code, Codex, Gemini CLI, Copilot — все используют OAuth 2.
|
||||
|
||||
- **Оценки LLM** — тестирование золотого набора с 10 предварительно загруженными вариантами, охватывающими приветствия, математику, географию, генерацию кода, соответствие JSON, перевод, уценку, отказ от безопасности.
|
||||
- **4 стратегии сопоставления** — `exact`, `contains`, `regex`, `custom` (функция JS)
|
||||
- **Тестовый стенд Translator Playground** — пакетное тестирование с несколькими входными данными и ожидаемыми результатами, сравнение между поставщиками.
|
||||
- **Тестовый стенд (Testbed)** — пакетное тестирование с несколькими входными данными и ожидаемыми результатами, сравнение между поставщиками.
|
||||
- **Тестер чата** — полный цикл с визуальным отображением ответов.
|
||||
- **Живой монитор** — поток всех запросов, проходящих через прокси, в реальном времени.
|
||||
|
||||
@@ -1006,7 +1016,7 @@ OmniRoute включает встроенный фреймворк оценки
|
||||
|
||||
- Приветствия, математика, география, генерация кода
|
||||
- Соответствие формату JSON, перевод, markdown
|
||||
- Отказ от небезопасного контента, подсчёт, булева логика
|
||||
- Отказ от небезопасного контента (Safety refusal), подсчёт, булева логика
|
||||
|
||||
### Стратегии оценки
|
||||
|
||||
|
||||
@@ -11,28 +11,6 @@ _Váš univerzálny proxy server API – jeden koncový bod, 36+ poskytovateľov
|
||||
|
||||
---
|
||||
|
||||
### 🆕 What's New in v2.7.0
|
||||
|
||||
- **Pluggable RouterStrategy** — rules, cost, and latency routing strategies
|
||||
- **Multilingual intent detection** — routing scoring in 30+ languages
|
||||
- **Request deduplication** — prevent duplicate API calls via content hash
|
||||
- **New providers:** Grok-4 Fast (xAI), GLM-5 / Z.AI, MiniMax M2.5, Kimi K2.5
|
||||
- **Updated pricing:** Grok-4 Fast $0.20/$0.50/M, GLM-5 $0.50/M, MiniMax M2.5 $0.30/M
|
||||
|
||||
---
|
||||
|
||||
### 🚀 New in v2.0.9+ — Playground, CLI Fingerprints & ACP
|
||||
|
||||
| Feature | What It Does |
|
||||
| ------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| 🎮 **Model Playground** | Dashboard page to test any model directly — provider/model/endpoint selectors, Monaco Editor, streaming, abort, timing |
|
||||
| 🔏 **CLI Fingerprint Matching** | Per-provider header/body ordering to match native CLI signatures — toggle per provider in Settings > Security. **Your proxy IP is preserved** |
|
||||
| 🤝 **ACP Support (Agent Client Protocol)** | CLI agent discovery (Codex, Claude, Goose, Gemini CLI, OpenClaw), process spawner, `/api/acp/agents` endpoint |
|
||||
| 🤖 **ACP Agents Dashboard** | Debug > Agents page — grid of 14 agents with install status, version, custom agent form for any CLI tool |
|
||||
| 🔧 **Custom Model `apiFormat` Routing** | Custom models with `apiFormat: "responses"` now correctly route to the Responses API translator |
|
||||
| 🏢 **Codex Workspace Isolation** | Multiple Codex workspaces per email — OAuth correctly separates connections by workspace ID |
|
||||
| 🔄 **Electron Auto-Update** | Desktop app checks for updates + auto-install on restart |
|
||||
|
||||
### 🤖 Bezplatný poskytovateľ AI pre vašich obľúbených kódovacích agentov
|
||||
|
||||
_Pripojte akýkoľvek nástroj IDE alebo CLI poháňaný AI cez OmniRoute – bezplatnú bránu API pre neobmedzené kódovanie._
|
||||
@@ -118,6 +96,38 @@ _Pripojte akýkoľvek nástroj IDE alebo CLI poháňaný AI cez OmniRoute – be
|
||||
|
||||
---
|
||||
|
||||
### 🆕 What's New in v3.0.0
|
||||
|
||||
| Area | Change |
|
||||
| --- | --- |
|
||||
| 🔒 **CodeQL Security** | Fixed 10+ CodeQL alerts: polynomial-redos, insecure-randomness, shell-injection |
|
||||
| ✅ **Route Validation** | All 176 API routes validated with Zod schemas + `validateBody()` |
|
||||
| 🐛 **omniModel Tag Leak** | Internal `<omniModel>` tags no longer leak to clients in SSE streams (#585) |
|
||||
| 🔑 **Registered Keys API** | Auto-provision API keys via `POST /api/v1/registered-keys` with quota enforcement |
|
||||
| 🎨 **Provider Icons** | 130+ provider logos via `@lobehub/icons` (SVG) with PNG fallback |
|
||||
| 🔄 **Model Auto-Sync** | 24h scheduler refreshes model lists for 16 providers |
|
||||
| 🌐 **OpenCode Zen/Go** | Two new providers: free tier + subscription tier |
|
||||
| 🔧 **926 Tests** | Full test suite passes with 0 failures |
|
||||
|
||||
|
||||
|
||||
### 🆕 What's New in v3.0.0
|
||||
|
||||
| Area | Change |
|
||||
| --- | --- |
|
||||
| 🔒 **CodeQL Security** | Fixed 10+ CodeQL alerts: polynomial-redos, insecure-randomness, shell-injection |
|
||||
| ✅ **Route Validation** | All 176 API routes validated with Zod schemas + `validateBody()` |
|
||||
| 🐛 **omniModel Tag Leak** | Internal `<omniModel>` tags no longer leak to clients in SSE streams (#585) |
|
||||
| 🔑 **Registered Keys API** | Auto-provision API keys via `POST /api/v1/registered-keys` with quota enforcement |
|
||||
| 🎨 **Provider Icons** | 130+ provider logos via `@lobehub/icons` (SVG) with PNG fallback |
|
||||
| 🔄 **Model Auto-Sync** | 24h scheduler refreshes model lists for 16 providers |
|
||||
| 🌐 **OpenCode Zen/Go** | Two new providers: free tier + subscription tier |
|
||||
| 🔧 **926 Tests** | Full test suite passes with 0 failures |
|
||||
|
||||
---
|
||||
|
||||
|
||||
|
||||
[](https://www.npmjs.com/package/omniroute)
|
||||
[](https://hub.docker.com/r/diegosouzapw/omniroute)
|
||||
[](https://github.com/diegosouzapw/OmniRoute/blob/main/LICENSE)
|
||||
|
||||
@@ -11,28 +11,6 @@ _Din universella API-proxy — en slutpunkt, 36+ leverantörer, noll driftstopp.
|
||||
|
||||
---
|
||||
|
||||
### 🆕 What's New in v2.7.0
|
||||
|
||||
- **Pluggable RouterStrategy** — rules, cost, and latency routing strategies
|
||||
- **Multilingual intent detection** — routing scoring in 30+ languages
|
||||
- **Request deduplication** — prevent duplicate API calls via content hash
|
||||
- **New providers:** Grok-4 Fast (xAI), GLM-5 / Z.AI, MiniMax M2.5, Kimi K2.5
|
||||
- **Updated pricing:** Grok-4 Fast $0.20/$0.50/M, GLM-5 $0.50/M, MiniMax M2.5 $0.30/M
|
||||
|
||||
---
|
||||
|
||||
### 🚀 New in v2.0.9+ — Playground, CLI Fingerprints & ACP
|
||||
|
||||
| Feature | What It Does |
|
||||
| ------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| 🎮 **Model Playground** | Dashboard page to test any model directly — provider/model/endpoint selectors, Monaco Editor, streaming, abort, timing |
|
||||
| 🔏 **CLI Fingerprint Matching** | Per-provider header/body ordering to match native CLI signatures — toggle per provider in Settings > Security. **Your proxy IP is preserved** |
|
||||
| 🤝 **ACP Support (Agent Client Protocol)** | CLI agent discovery (Codex, Claude, Goose, Gemini CLI, OpenClaw), process spawner, `/api/acp/agents` endpoint |
|
||||
| 🤖 **ACP Agents Dashboard** | Debug > Agents page — grid of 14 agents with install status, version, custom agent form for any CLI tool |
|
||||
| 🔧 **Custom Model `apiFormat` Routing** | Custom models with `apiFormat: "responses"` now correctly route to the Responses API translator |
|
||||
| 🏢 **Codex Workspace Isolation** | Multiple Codex workspaces per email — OAuth correctly separates connections by workspace ID |
|
||||
| 🔄 **Electron Auto-Update** | Desktop app checks for updates + auto-install on restart |
|
||||
|
||||
### 🤖 Gratis AI-leverantör för dina favoritkodningsagenter
|
||||
|
||||
_Anslut alla AI-drivna IDE- eller CLI-verktyg via OmniRoute — gratis API-gateway för obegränsad kodning._
|
||||
@@ -118,6 +96,38 @@ _Anslut alla AI-drivna IDE- eller CLI-verktyg via OmniRoute — gratis API-gatew
|
||||
|
||||
---
|
||||
|
||||
### 🆕 What's New in v3.0.0
|
||||
|
||||
| Area | Change |
|
||||
| --- | --- |
|
||||
| 🔒 **CodeQL Security** | Fixed 10+ CodeQL alerts: polynomial-redos, insecure-randomness, shell-injection |
|
||||
| ✅ **Route Validation** | All 176 API routes validated with Zod schemas + `validateBody()` |
|
||||
| 🐛 **omniModel Tag Leak** | Internal `<omniModel>` tags no longer leak to clients in SSE streams (#585) |
|
||||
| 🔑 **Registered Keys API** | Auto-provision API keys via `POST /api/v1/registered-keys` with quota enforcement |
|
||||
| 🎨 **Provider Icons** | 130+ provider logos via `@lobehub/icons` (SVG) with PNG fallback |
|
||||
| 🔄 **Model Auto-Sync** | 24h scheduler refreshes model lists for 16 providers |
|
||||
| 🌐 **OpenCode Zen/Go** | Two new providers: free tier + subscription tier |
|
||||
| 🔧 **926 Tests** | Full test suite passes with 0 failures |
|
||||
|
||||
|
||||
|
||||
### 🆕 What's New in v3.0.0
|
||||
|
||||
| Area | Change |
|
||||
| --- | --- |
|
||||
| 🔒 **CodeQL Security** | Fixed 10+ CodeQL alerts: polynomial-redos, insecure-randomness, shell-injection |
|
||||
| ✅ **Route Validation** | All 176 API routes validated with Zod schemas + `validateBody()` |
|
||||
| 🐛 **omniModel Tag Leak** | Internal `<omniModel>` tags no longer leak to clients in SSE streams (#585) |
|
||||
| 🔑 **Registered Keys API** | Auto-provision API keys via `POST /api/v1/registered-keys` with quota enforcement |
|
||||
| 🎨 **Provider Icons** | 130+ provider logos via `@lobehub/icons` (SVG) with PNG fallback |
|
||||
| 🔄 **Model Auto-Sync** | 24h scheduler refreshes model lists for 16 providers |
|
||||
| 🌐 **OpenCode Zen/Go** | Two new providers: free tier + subscription tier |
|
||||
| 🔧 **926 Tests** | Full test suite passes with 0 failures |
|
||||
|
||||
---
|
||||
|
||||
|
||||
|
||||
[](https://www.npmjs.com/package/omniroute)
|
||||
[](https://hub.docker.com/r/diegosouzapw/omniroute)
|
||||
[](https://github.com/diegosouzapw/OmniRoute/blob/main/LICENSE)
|
||||
|
||||
@@ -11,28 +11,6 @@ _พร็อกซี API สากลของคุณ — จุดสิ้
|
||||
|
||||
---
|
||||
|
||||
### 🆕 What's New in v2.7.0
|
||||
|
||||
- **Pluggable RouterStrategy** — rules, cost, and latency routing strategies
|
||||
- **Multilingual intent detection** — routing scoring in 30+ languages
|
||||
- **Request deduplication** — prevent duplicate API calls via content hash
|
||||
- **New providers:** Grok-4 Fast (xAI), GLM-5 / Z.AI, MiniMax M2.5, Kimi K2.5
|
||||
- **Updated pricing:** Grok-4 Fast $0.20/$0.50/M, GLM-5 $0.50/M, MiniMax M2.5 $0.30/M
|
||||
|
||||
---
|
||||
|
||||
### 🚀 New in v2.0.9+ — Playground, CLI Fingerprints & ACP
|
||||
|
||||
| Feature | What It Does |
|
||||
| ------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| 🎮 **Model Playground** | Dashboard page to test any model directly — provider/model/endpoint selectors, Monaco Editor, streaming, abort, timing |
|
||||
| 🔏 **CLI Fingerprint Matching** | Per-provider header/body ordering to match native CLI signatures — toggle per provider in Settings > Security. **Your proxy IP is preserved** |
|
||||
| 🤝 **ACP Support (Agent Client Protocol)** | CLI agent discovery (Codex, Claude, Goose, Gemini CLI, OpenClaw), process spawner, `/api/acp/agents` endpoint |
|
||||
| 🤖 **ACP Agents Dashboard** | Debug > Agents page — grid of 14 agents with install status, version, custom agent form for any CLI tool |
|
||||
| 🔧 **Custom Model `apiFormat` Routing** | Custom models with `apiFormat: "responses"` now correctly route to the Responses API translator |
|
||||
| 🏢 **Codex Workspace Isolation** | Multiple Codex workspaces per email — OAuth correctly separates connections by workspace ID |
|
||||
| 🔄 **Electron Auto-Update** | Desktop app checks for updates + auto-install on restart |
|
||||
|
||||
### 🤖 ผู้ให้บริการ AI ฟรีสำหรับตัวแทนการเขียนโค้ดที่คุณชื่นชอบ
|
||||
|
||||
_เชื่อมต่อเครื่องมือ IDE หรือ CLI ที่ขับเคลื่อนด้วย AI ผ่าน OmniRoute — เกตเวย์ API ฟรีสำหรับการเข้ารหัสไม่จำกัด_
|
||||
@@ -118,6 +96,38 @@ _เชื่อมต่อเครื่องมือ IDE หรือ CLI
|
||||
|
||||
---
|
||||
|
||||
### 🆕 What's New in v3.0.0
|
||||
|
||||
| Area | Change |
|
||||
| --- | --- |
|
||||
| 🔒 **CodeQL Security** | Fixed 10+ CodeQL alerts: polynomial-redos, insecure-randomness, shell-injection |
|
||||
| ✅ **Route Validation** | All 176 API routes validated with Zod schemas + `validateBody()` |
|
||||
| 🐛 **omniModel Tag Leak** | Internal `<omniModel>` tags no longer leak to clients in SSE streams (#585) |
|
||||
| 🔑 **Registered Keys API** | Auto-provision API keys via `POST /api/v1/registered-keys` with quota enforcement |
|
||||
| 🎨 **Provider Icons** | 130+ provider logos via `@lobehub/icons` (SVG) with PNG fallback |
|
||||
| 🔄 **Model Auto-Sync** | 24h scheduler refreshes model lists for 16 providers |
|
||||
| 🌐 **OpenCode Zen/Go** | Two new providers: free tier + subscription tier |
|
||||
| 🔧 **926 Tests** | Full test suite passes with 0 failures |
|
||||
|
||||
|
||||
|
||||
### 🆕 What's New in v3.0.0
|
||||
|
||||
| Area | Change |
|
||||
| --- | --- |
|
||||
| 🔒 **CodeQL Security** | Fixed 10+ CodeQL alerts: polynomial-redos, insecure-randomness, shell-injection |
|
||||
| ✅ **Route Validation** | All 176 API routes validated with Zod schemas + `validateBody()` |
|
||||
| 🐛 **omniModel Tag Leak** | Internal `<omniModel>` tags no longer leak to clients in SSE streams (#585) |
|
||||
| 🔑 **Registered Keys API** | Auto-provision API keys via `POST /api/v1/registered-keys` with quota enforcement |
|
||||
| 🎨 **Provider Icons** | 130+ provider logos via `@lobehub/icons` (SVG) with PNG fallback |
|
||||
| 🔄 **Model Auto-Sync** | 24h scheduler refreshes model lists for 16 providers |
|
||||
| 🌐 **OpenCode Zen/Go** | Two new providers: free tier + subscription tier |
|
||||
| 🔧 **926 Tests** | Full test suite passes with 0 failures |
|
||||
|
||||
---
|
||||
|
||||
|
||||
|
||||
[](https://www.npmjs.com/package/omniroute)
|
||||
[](https://hub.docker.com/r/diegosouzapw/omniroute)
|
||||
[](https://github.com/diegosouzapw/OmniRoute/blob/main/LICENSE)
|
||||
|
||||
@@ -11,28 +11,6 @@ _Ваш універсальний API-проксі — одна кінцева
|
||||
|
||||
---
|
||||
|
||||
### 🆕 What's New in v2.7.0
|
||||
|
||||
- **Pluggable RouterStrategy** — rules, cost, and latency routing strategies
|
||||
- **Multilingual intent detection** — routing scoring in 30+ languages
|
||||
- **Request deduplication** — prevent duplicate API calls via content hash
|
||||
- **New providers:** Grok-4 Fast (xAI), GLM-5 / Z.AI, MiniMax M2.5, Kimi K2.5
|
||||
- **Updated pricing:** Grok-4 Fast $0.20/$0.50/M, GLM-5 $0.50/M, MiniMax M2.5 $0.30/M
|
||||
|
||||
---
|
||||
|
||||
### 🚀 New in v2.0.9+ — Playground, CLI Fingerprints & ACP
|
||||
|
||||
| Feature | What It Does |
|
||||
| ------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| 🎮 **Model Playground** | Dashboard page to test any model directly — provider/model/endpoint selectors, Monaco Editor, streaming, abort, timing |
|
||||
| 🔏 **CLI Fingerprint Matching** | Per-provider header/body ordering to match native CLI signatures — toggle per provider in Settings > Security. **Your proxy IP is preserved** |
|
||||
| 🤝 **ACP Support (Agent Client Protocol)** | CLI agent discovery (Codex, Claude, Goose, Gemini CLI, OpenClaw), process spawner, `/api/acp/agents` endpoint |
|
||||
| 🤖 **ACP Agents Dashboard** | Debug > Agents page — grid of 14 agents with install status, version, custom agent form for any CLI tool |
|
||||
| 🔧 **Custom Model `apiFormat` Routing** | Custom models with `apiFormat: "responses"` now correctly route to the Responses API translator |
|
||||
| 🏢 **Codex Workspace Isolation** | Multiple Codex workspaces per email — OAuth correctly separates connections by workspace ID |
|
||||
| 🔄 **Electron Auto-Update** | Desktop app checks for updates + auto-install on restart |
|
||||
|
||||
### 🤖 Безкоштовний постачальник AI для ваших улюблених агентів кодування
|
||||
|
||||
_Підключіть будь-який інструмент IDE або CLI на основі штучного інтелекту через OmniRoute — безкоштовний шлюз API для необмеженого програмування._
|
||||
@@ -118,6 +96,38 @@ _Підключіть будь-який інструмент IDE або CLI на
|
||||
|
||||
---
|
||||
|
||||
### 🆕 What's New in v3.0.0
|
||||
|
||||
| Area | Change |
|
||||
| --- | --- |
|
||||
| 🔒 **CodeQL Security** | Fixed 10+ CodeQL alerts: polynomial-redos, insecure-randomness, shell-injection |
|
||||
| ✅ **Route Validation** | All 176 API routes validated with Zod schemas + `validateBody()` |
|
||||
| 🐛 **omniModel Tag Leak** | Internal `<omniModel>` tags no longer leak to clients in SSE streams (#585) |
|
||||
| 🔑 **Registered Keys API** | Auto-provision API keys via `POST /api/v1/registered-keys` with quota enforcement |
|
||||
| 🎨 **Provider Icons** | 130+ provider logos via `@lobehub/icons` (SVG) with PNG fallback |
|
||||
| 🔄 **Model Auto-Sync** | 24h scheduler refreshes model lists for 16 providers |
|
||||
| 🌐 **OpenCode Zen/Go** | Two new providers: free tier + subscription tier |
|
||||
| 🔧 **926 Tests** | Full test suite passes with 0 failures |
|
||||
|
||||
|
||||
|
||||
### 🆕 What's New in v3.0.0
|
||||
|
||||
| Area | Change |
|
||||
| --- | --- |
|
||||
| 🔒 **CodeQL Security** | Fixed 10+ CodeQL alerts: polynomial-redos, insecure-randomness, shell-injection |
|
||||
| ✅ **Route Validation** | All 176 API routes validated with Zod schemas + `validateBody()` |
|
||||
| 🐛 **omniModel Tag Leak** | Internal `<omniModel>` tags no longer leak to clients in SSE streams (#585) |
|
||||
| 🔑 **Registered Keys API** | Auto-provision API keys via `POST /api/v1/registered-keys` with quota enforcement |
|
||||
| 🎨 **Provider Icons** | 130+ provider logos via `@lobehub/icons` (SVG) with PNG fallback |
|
||||
| 🔄 **Model Auto-Sync** | 24h scheduler refreshes model lists for 16 providers |
|
||||
| 🌐 **OpenCode Zen/Go** | Two new providers: free tier + subscription tier |
|
||||
| 🔧 **926 Tests** | Full test suite passes with 0 failures |
|
||||
|
||||
---
|
||||
|
||||
|
||||
|
||||
[](https://www.npmjs.com/package/omniroute)
|
||||
[](https://hub.docker.com/r/diegosouzapw/omniroute)
|
||||
[](https://github.com/diegosouzapw/OmniRoute/blob/main/LICENSE)
|
||||
|
||||
@@ -11,28 +11,6 @@ _Proxy API phổ quát của bạn — một điểm cuối, hơn 36 nhà cung c
|
||||
|
||||
---
|
||||
|
||||
### 🆕 What's New in v2.7.0
|
||||
|
||||
- **Pluggable RouterStrategy** — rules, cost, and latency routing strategies
|
||||
- **Multilingual intent detection** — routing scoring in 30+ languages
|
||||
- **Request deduplication** — prevent duplicate API calls via content hash
|
||||
- **New providers:** Grok-4 Fast (xAI), GLM-5 / Z.AI, MiniMax M2.5, Kimi K2.5
|
||||
- **Updated pricing:** Grok-4 Fast $0.20/$0.50/M, GLM-5 $0.50/M, MiniMax M2.5 $0.30/M
|
||||
|
||||
---
|
||||
|
||||
### 🚀 New in v2.0.9+ — Playground, CLI Fingerprints & ACP
|
||||
|
||||
| Feature | What It Does |
|
||||
| ------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| 🎮 **Model Playground** | Dashboard page to test any model directly — provider/model/endpoint selectors, Monaco Editor, streaming, abort, timing |
|
||||
| 🔏 **CLI Fingerprint Matching** | Per-provider header/body ordering to match native CLI signatures — toggle per provider in Settings > Security. **Your proxy IP is preserved** |
|
||||
| 🤝 **ACP Support (Agent Client Protocol)** | CLI agent discovery (Codex, Claude, Goose, Gemini CLI, OpenClaw), process spawner, `/api/acp/agents` endpoint |
|
||||
| 🤖 **ACP Agents Dashboard** | Debug > Agents page — grid of 14 agents with install status, version, custom agent form for any CLI tool |
|
||||
| 🔧 **Custom Model `apiFormat` Routing** | Custom models with `apiFormat: "responses"` now correctly route to the Responses API translator |
|
||||
| 🏢 **Codex Workspace Isolation** | Multiple Codex workspaces per email — OAuth correctly separates connections by workspace ID |
|
||||
| 🔄 **Electron Auto-Update** | Desktop app checks for updates + auto-install on restart |
|
||||
|
||||
### 🤖 Nhà cung cấp AI miễn phí cho các tác nhân mã hóa yêu thích của bạn
|
||||
|
||||
_Kết nối mọi công cụ IDE hoặc CLI được hỗ trợ bởi AI thông qua OmniRoute — cổng API miễn phí để mã hóa không giới hạn._
|
||||
@@ -118,6 +96,38 @@ _Kết nối mọi công cụ IDE hoặc CLI được hỗ trợ bởi AI thông
|
||||
|
||||
---
|
||||
|
||||
### 🆕 What's New in v3.0.0
|
||||
|
||||
| Area | Change |
|
||||
| --- | --- |
|
||||
| 🔒 **CodeQL Security** | Fixed 10+ CodeQL alerts: polynomial-redos, insecure-randomness, shell-injection |
|
||||
| ✅ **Route Validation** | All 176 API routes validated with Zod schemas + `validateBody()` |
|
||||
| 🐛 **omniModel Tag Leak** | Internal `<omniModel>` tags no longer leak to clients in SSE streams (#585) |
|
||||
| 🔑 **Registered Keys API** | Auto-provision API keys via `POST /api/v1/registered-keys` with quota enforcement |
|
||||
| 🎨 **Provider Icons** | 130+ provider logos via `@lobehub/icons` (SVG) with PNG fallback |
|
||||
| 🔄 **Model Auto-Sync** | 24h scheduler refreshes model lists for 16 providers |
|
||||
| 🌐 **OpenCode Zen/Go** | Two new providers: free tier + subscription tier |
|
||||
| 🔧 **926 Tests** | Full test suite passes with 0 failures |
|
||||
|
||||
|
||||
|
||||
### 🆕 What's New in v3.0.0
|
||||
|
||||
| Area | Change |
|
||||
| --- | --- |
|
||||
| 🔒 **CodeQL Security** | Fixed 10+ CodeQL alerts: polynomial-redos, insecure-randomness, shell-injection |
|
||||
| ✅ **Route Validation** | All 176 API routes validated with Zod schemas + `validateBody()` |
|
||||
| 🐛 **omniModel Tag Leak** | Internal `<omniModel>` tags no longer leak to clients in SSE streams (#585) |
|
||||
| 🔑 **Registered Keys API** | Auto-provision API keys via `POST /api/v1/registered-keys` with quota enforcement |
|
||||
| 🎨 **Provider Icons** | 130+ provider logos via `@lobehub/icons` (SVG) with PNG fallback |
|
||||
| 🔄 **Model Auto-Sync** | 24h scheduler refreshes model lists for 16 providers |
|
||||
| 🌐 **OpenCode Zen/Go** | Two new providers: free tier + subscription tier |
|
||||
| 🔧 **926 Tests** | Full test suite passes with 0 failures |
|
||||
|
||||
---
|
||||
|
||||
|
||||
|
||||
[](https://www.npmjs.com/package/omniroute)
|
||||
[](https://hub.docker.com/r/diegosouzapw/omniroute)
|
||||
[](https://github.com/diegosouzapw/OmniRoute/blob/main/LICENSE)
|
||||
|
||||
@@ -11,28 +11,6 @@ _您的通用 API 代理 — 一个端点,36+ 提供商,零停机时间。_
|
||||
|
||||
---
|
||||
|
||||
### 🆕 v2.7.0 新功能
|
||||
|
||||
- **可插拔 RouterStrategy** — 支持规则、成本和延迟策略
|
||||
- **多语言意图检测** — 支持 30+ 语言的路由评分
|
||||
- **请求去重** — 基于内容哈希避免重复 API 调用
|
||||
- **新增提供商:** Grok-4 Fast (xAI)、GLM-5 / Z.AI、MiniMax M2.5、Kimi K2.5
|
||||
- **价格更新:** Grok-4 Fast $0.20/$0.50/M,GLM-5 $0.50/M,MiniMax M2.5 $0.30/M
|
||||
|
||||
---
|
||||
|
||||
### 🚀 New in v2.0.9+ — Playground, CLI Fingerprints & ACP
|
||||
|
||||
| Feature | What It Does |
|
||||
| ------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| 🎮 **Model Playground** | Dashboard page to test any model directly — provider/model/endpoint selectors, Monaco Editor, streaming, abort, timing |
|
||||
| 🔏 **CLI Fingerprint Matching** | Per-provider header/body ordering to match native CLI signatures — toggle per provider in Settings > Security. **Your proxy IP is preserved** |
|
||||
| 🤝 **ACP Support (Agent Client Protocol)** | CLI agent discovery (Codex, Claude, Goose, Gemini CLI, OpenClaw), process spawner, `/api/acp/agents` endpoint |
|
||||
| 🤖 **ACP Agents Dashboard** | Debug > Agents page — grid of 14 agents with install status, version, custom agent form for any CLI tool |
|
||||
| 🔧 **Custom Model `apiFormat` Routing** | Custom models with `apiFormat: "responses"` now correctly route to the Responses API translator |
|
||||
| 🏢 **Codex Workspace Isolation** | Multiple Codex workspaces per email — OAuth correctly separates connections by workspace ID |
|
||||
| 🔄 **Electron Auto-Update** | Desktop app checks for updates + auto-install on restart |
|
||||
|
||||
### 🤖 为您最爱的编程代理提供免费 AI
|
||||
|
||||
_通过 OmniRoute 连接任何 AI 驱动的 IDE 或 CLI 工具 — 免费 API 网关,无限编程。_
|
||||
@@ -118,6 +96,38 @@ _通过 OmniRoute 连接任何 AI 驱动的 IDE 或 CLI 工具 — 免费 API
|
||||
|
||||
---
|
||||
|
||||
### 🆕 What's New in v3.0.0
|
||||
|
||||
| Area | Change |
|
||||
| --- | --- |
|
||||
| 🔒 **CodeQL Security** | Fixed 10+ CodeQL alerts: polynomial-redos, insecure-randomness, shell-injection |
|
||||
| ✅ **Route Validation** | All 176 API routes validated with Zod schemas + `validateBody()` |
|
||||
| 🐛 **omniModel Tag Leak** | Internal `<omniModel>` tags no longer leak to clients in SSE streams (#585) |
|
||||
| 🔑 **Registered Keys API** | Auto-provision API keys via `POST /api/v1/registered-keys` with quota enforcement |
|
||||
| 🎨 **Provider Icons** | 130+ provider logos via `@lobehub/icons` (SVG) with PNG fallback |
|
||||
| 🔄 **Model Auto-Sync** | 24h scheduler refreshes model lists for 16 providers |
|
||||
| 🌐 **OpenCode Zen/Go** | Two new providers: free tier + subscription tier |
|
||||
| 🔧 **926 Tests** | Full test suite passes with 0 failures |
|
||||
|
||||
|
||||
|
||||
### 🆕 What's New in v3.0.0
|
||||
|
||||
| Area | Change |
|
||||
| --- | --- |
|
||||
| 🔒 **CodeQL Security** | Fixed 10+ CodeQL alerts: polynomial-redos, insecure-randomness, shell-injection |
|
||||
| ✅ **Route Validation** | All 176 API routes validated with Zod schemas + `validateBody()` |
|
||||
| 🐛 **omniModel Tag Leak** | Internal `<omniModel>` tags no longer leak to clients in SSE streams (#585) |
|
||||
| 🔑 **Registered Keys API** | Auto-provision API keys via `POST /api/v1/registered-keys` with quota enforcement |
|
||||
| 🎨 **Provider Icons** | 130+ provider logos via `@lobehub/icons` (SVG) with PNG fallback |
|
||||
| 🔄 **Model Auto-Sync** | 24h scheduler refreshes model lists for 16 providers |
|
||||
| 🌐 **OpenCode Zen/Go** | Two new providers: free tier + subscription tier |
|
||||
| 🔧 **926 Tests** | Full test suite passes with 0 failures |
|
||||
|
||||
---
|
||||
|
||||
|
||||
|
||||
[](https://www.npmjs.com/package/omniroute)
|
||||
[](https://hub.docker.com/r/diegosouzapw/omniroute)
|
||||
[](https://github.com/diegosouzapw/OmniRoute/blob/main/LICENSE)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
openapi: 3.1.0
|
||||
info:
|
||||
title: OmniRoute API
|
||||
version: 3.0.0-rc.15
|
||||
version: 3.0.1
|
||||
description: |
|
||||
OmniRoute is a local-first AI API proxy router. It provides an OpenAI-compatible
|
||||
endpoint that routes requests to multiple AI providers with load balancing,
|
||||
|
||||
@@ -1,280 +0,0 @@
|
||||
# Zed IDE OAuth Import - Documentation
|
||||
|
||||
## Overview
|
||||
|
||||
OmniRoute can automatically import OAuth credentials from Zed IDE by accessing the operating system's secure keychain storage. This eliminates manual credential copying and enables seamless integration between Zed IDE and OmniRoute.
|
||||
|
||||
## How It Works
|
||||
|
||||
Zed IDE stores all OAuth tokens in your operating system's native credential storage:
|
||||
- **macOS**: Keychain Access
|
||||
- **Windows**: Credential Manager
|
||||
- **Linux**: libsecret / GNOME Keyring
|
||||
|
||||
As documented in [Zed's official documentation](https://zed.dev/docs/ai/llm-providers):
|
||||
> "API keys are not stored as plain text in your settings file, but rather in your OS's secure credential storage."
|
||||
|
||||
OmniRoute uses the `keytar` library to securely read these credentials with your permission.
|
||||
|
||||
## Supported Providers
|
||||
|
||||
The following Zed IDE providers can be imported:
|
||||
- OpenAI
|
||||
- Anthropic (Claude)
|
||||
- Google AI (Gemini)
|
||||
- Mistral
|
||||
- xAI (Grok)
|
||||
- OpenRouter
|
||||
- DeepSeek
|
||||
|
||||
## Installation
|
||||
|
||||
### Prerequisites
|
||||
|
||||
**Linux users** must install libsecret development files:
|
||||
|
||||
```bash
|
||||
# Debian/Ubuntu
|
||||
sudo apt-get install libsecret-1-dev
|
||||
|
||||
# Red Hat/Fedora
|
||||
sudo yum install libsecret-devel
|
||||
|
||||
# Arch Linux
|
||||
sudo pacman -S libsecret
|
||||
```
|
||||
|
||||
**macOS and Windows** users don't need additional dependencies.
|
||||
|
||||
### Install Dependencies
|
||||
|
||||
```bash
|
||||
npm install keytar
|
||||
```
|
||||
|
||||
Or using pnpm:
|
||||
|
||||
```bash
|
||||
pnpm install keytar
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
### API Endpoint
|
||||
|
||||
**Endpoint**: `POST /api/providers/zed/import`
|
||||
|
||||
**Request**:
|
||||
```bash
|
||||
curl -X POST http://localhost:20128/api/providers/zed/import \
|
||||
-H "Content-Type: application/json"
|
||||
```
|
||||
|
||||
**Response** (success):
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"count": 3,
|
||||
"providers": ["openai", "anthropic", "google"],
|
||||
"zedInstalled": true
|
||||
}
|
||||
```
|
||||
|
||||
**Response** (Zed not installed):
|
||||
```json
|
||||
{
|
||||
"success": false,
|
||||
"error": "Zed IDE does not appear to be installed on this system.",
|
||||
"zedInstalled": false
|
||||
}
|
||||
```
|
||||
|
||||
**Response** (permission denied):
|
||||
```json
|
||||
{
|
||||
"success": false,
|
||||
"error": "Keychain access denied. Please grant permission when prompted by your OS."
|
||||
}
|
||||
```
|
||||
|
||||
### Programmatic Usage
|
||||
|
||||
```typescript
|
||||
import {
|
||||
discoverZedCredentials,
|
||||
getZedCredential,
|
||||
isZedInstalled
|
||||
} from '@/lib/zed-oauth/keychain-reader';
|
||||
|
||||
// Check if Zed is installed
|
||||
const installed = await isZedInstalled();
|
||||
|
||||
// Discover all credentials
|
||||
const credentials = await discoverZedCredentials();
|
||||
console.log(`Found ${credentials.length} credentials`);
|
||||
|
||||
// Get specific provider
|
||||
const openaiCred = await getZedCredential('openai');
|
||||
if (openaiCred) {
|
||||
console.log(`OpenAI token: ${openaiCred.token.substring(0, 10)}...`);
|
||||
}
|
||||
```
|
||||
|
||||
## Security
|
||||
|
||||
### Permission Prompt
|
||||
|
||||
The first time OmniRoute accesses the keychain, your operating system will prompt for permission:
|
||||
|
||||
- **macOS**: "OmniRoute wants to access your keychain"
|
||||
- **Windows**: UAC prompt or Credential Manager authorization
|
||||
- **Linux**: "Authentication required to access the default keyring"
|
||||
|
||||
You can grant:
|
||||
- **Allow Once**: Permission for this session only
|
||||
- **Always Allow**: Permanent access (until revoked)
|
||||
- **Deny**: Credential import will fail
|
||||
|
||||
### Data Handling
|
||||
|
||||
1. **No Master Password Storage**: OmniRoute never stores your keychain master password
|
||||
2. **Minimal Access**: Only reads Zed-specific credential entries
|
||||
3. **Encryption at Rest**: Imported tokens are encrypted using AES-256-GCM in OmniRoute's database
|
||||
4. **Audit Logging**: All import attempts are logged for security tracking
|
||||
|
||||
### Revoking Access
|
||||
|
||||
To revoke OmniRoute's keychain access:
|
||||
|
||||
**macOS**:
|
||||
1. Open **Keychain Access** app
|
||||
2. Go to **Keychain Access** → **Preferences** → **Access Control**
|
||||
3. Remove OmniRoute from the allowed applications list
|
||||
|
||||
**Windows**:
|
||||
1. Open **Credential Manager**
|
||||
2. Find OmniRoute entries
|
||||
3. Remove or modify permissions
|
||||
|
||||
**Linux (GNOME)**:
|
||||
1. Open **Seahorse** (Passwords and Keys)
|
||||
2. Find OmniRoute entries under Login keyring
|
||||
3. Remove or edit access control
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### "Keychain access denied" Error
|
||||
|
||||
**Cause**: User denied permission prompt or previous denial cached.
|
||||
|
||||
**Solution**:
|
||||
1. Retry the import (permission prompt will appear again)
|
||||
2. Check system keychain settings (see "Revoking Access" section)
|
||||
3. On macOS, restart Keychain Access app
|
||||
|
||||
### "Keychain service not available" Error
|
||||
|
||||
**Cause**: OS credential storage not configured or missing dependencies.
|
||||
|
||||
**Solution** (Linux):
|
||||
```bash
|
||||
# Install libsecret
|
||||
sudo apt-get install libsecret-1-dev
|
||||
|
||||
# Ensure keyring daemon is running
|
||||
systemctl --user status gnome-keyring-daemon
|
||||
```
|
||||
|
||||
### "Zed IDE does not appear to be installed"
|
||||
|
||||
**Cause**: Zed config directory not found in expected locations.
|
||||
|
||||
**Solution**:
|
||||
- Verify Zed is installed: `zed --version`
|
||||
- Check config exists at:
|
||||
- Linux: `~/.config/zed`
|
||||
- macOS: `~/Library/Application Support/Zed`
|
||||
- Windows: `%APPDATA%\Zed`
|
||||
|
||||
### No Credentials Found
|
||||
|
||||
**Cause**: Zed hasn't stored OAuth tokens yet, or using API keys instead of OAuth.
|
||||
|
||||
**Solution**:
|
||||
1. Open Zed IDE
|
||||
2. Go to Agent Panel settings (⌘/Ctrl+Shift+P → "agent: open settings")
|
||||
3. Add at least one provider with OAuth/API key
|
||||
4. Retry import in OmniRoute
|
||||
|
||||
## Command-Line Alternatives
|
||||
|
||||
For advanced users who prefer manual extraction:
|
||||
|
||||
### macOS
|
||||
|
||||
```bash
|
||||
# Find OpenAI token
|
||||
security find-generic-password -s "zed-openai" -w
|
||||
|
||||
# List all Zed credentials
|
||||
security dump-keychain | grep -i "zed"
|
||||
```
|
||||
|
||||
### Linux (GNOME Keyring)
|
||||
|
||||
```bash
|
||||
# Using secret-tool
|
||||
secret-tool lookup service zed-openai
|
||||
|
||||
# List all Zed entries
|
||||
secret-tool search service zed
|
||||
```
|
||||
|
||||
### Windows (PowerShell)
|
||||
|
||||
```powershell
|
||||
# List Zed credentials
|
||||
cmdkey /list | Select-String "zed"
|
||||
```
|
||||
|
||||
## Technical Reference
|
||||
|
||||
### Service Name Patterns
|
||||
|
||||
Zed IDE uses these service names for keychain storage:
|
||||
|
||||
| Provider | Service Names |
|
||||
|----------|--------------|
|
||||
| OpenAI | `zed-openai`, `ai.zed.openai`, `Zed-OpenAI` |
|
||||
| Anthropic | `zed-anthropic`, `ai.zed.anthropic`, `Zed-Anthropic` |
|
||||
| Google AI | `zed-google`, `ai.zed.google`, `Zed-Google` |
|
||||
| Mistral | `zed-mistral`, `ai.zed.mistral`, `Zed-Mistral` |
|
||||
| xAI | `zed-xai`, `ai.zed.xai`, `Zed-xAI` |
|
||||
| OpenRouter | `zed-openrouter`, `ai.zed.openrouter`, `Zed-OpenRouter` |
|
||||
| DeepSeek | `zed-deepseek`, `ai.zed.deepseek`, `Zed-DeepSeek` |
|
||||
|
||||
### keytar API
|
||||
|
||||
```typescript
|
||||
// Get password for service+account
|
||||
const token = await keytar.getPassword('service-name', 'account-name');
|
||||
|
||||
// Find all credentials for a service
|
||||
const credentials = await keytar.findCredentials('service-name');
|
||||
|
||||
// Set password (not used in import, but available)
|
||||
await keytar.setPassword('service-name', 'account-name', 'password');
|
||||
```
|
||||
|
||||
## References
|
||||
|
||||
- [Zed IDE LLM Providers Documentation](https://zed.dev/docs/ai/llm-providers)
|
||||
- [keytar Library on GitHub](https://github.com/atom/node-keytar)
|
||||
- [VS Code Secret Storage](https://code.visualstudio.com/api/references/vscode-api#SecretStorage)
|
||||
- [GitHub Copilot CLI Authentication](https://docs.github.com/en/copilot/how-tos/copilot-cli/set-up-copilot-cli/authenticate-copilot-cli)
|
||||
|
||||
## Support
|
||||
|
||||
For issues or questions:
|
||||
- Open an issue on [OmniRoute GitHub](https://github.com/diegosouzapw/OmniRoute/issues)
|
||||
- Join the [WhatsApp Community](https://chat.whatsapp.com/JI7cDQ1GyaiDHhVBpLxf8b?mode=gi_t)
|
||||
@@ -29,6 +29,7 @@ const eslintConfig = [
|
||||
ignores: [
|
||||
// Next.js build output
|
||||
".next/**",
|
||||
"src/.next/**",
|
||||
"out/**",
|
||||
"build/**",
|
||||
"next-env.d.ts",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# OmniRoute
|
||||
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 36+ AI providers — all through a single OpenAI-compatible endpoint.
|
||||
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 67+ AI providers — all through a single OpenAI-compatible endpoint.
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -8,19 +8,19 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
|
||||
**Key value:** One endpoint (`http://localhost:20128/v1`), unlimited models, zero downtime, minimal cost.
|
||||
|
||||
**Current version:** 2.0.13
|
||||
**Current version:** 3.0.0
|
||||
|
||||
## Tech Stack
|
||||
|
||||
- **Runtime:** Node.js >= 18
|
||||
- **Framework:** Next.js 16 (App Router) with TypeScript
|
||||
- **Framework:** Next.js 16 (App Router) with TypeScript 5.9
|
||||
- **Database:** SQLite via better-sqlite3 (local, zero-config)
|
||||
- **State management:** Zustand (client), lowdb (server JSON persistence)
|
||||
- **UI:** React 19, Tailwind CSS 4, Recharts for analytics
|
||||
- **State management:** Zustand (client), SQLite (server persistence)
|
||||
- **UI:** React 19, Tailwind CSS 4, Recharts for analytics, @lobehub/icons for 130+ provider SVG icons
|
||||
- **Auth:** OAuth 2.0 (PKCE) for providers, bcrypt for local user auth
|
||||
- **Background jobs:** Custom token health check scheduler
|
||||
- **Background jobs:** Custom token health check scheduler, 24h model auto-sync
|
||||
- **Streaming:** Server-Sent Events (SSE) for real-time proxy responses
|
||||
- **Proxy engine:** Custom pipeline with format translation, circuit breaker, rate limiting
|
||||
- **Proxy engine:** Custom pipeline with format translation, circuit breaker, rate limiting, auto-combo engine
|
||||
- **i18n:** next-intl with 30 languages
|
||||
- **Package:** Published on npm (`omniroute`) and Docker Hub (`diegosouzapw/omniroute`)
|
||||
|
||||
@@ -35,14 +35,14 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
│ │ │ ├── agents/ # ACP Agents dashboard (CLI agent detection + custom agents)
|
||||
│ │ │ ├── analytics/ # Usage analytics and charts
|
||||
│ │ │ ├── api-manager/ # API key management
|
||||
│ │ │ ├── cli-tools/ # CLI tool configuration (Claude, Codex, Gemini, etc.)
|
||||
│ │ │ ├── combos/ # Model combo management
|
||||
│ │ │ ├── costs/ # Cost tracking
|
||||
│ │ │ ├── endpoint/ # Endpoint info and cloud proxy
|
||||
│ │ │ ├── health/ # System health monitoring
|
||||
│ │ │ ├── cli-tools/ # CLI tool configuration (Claude Code, Codex, Gemini CLI, etc.)
|
||||
│ │ │ ├── combos/ # Model combo management (9 strategies + 4 templates)
|
||||
│ │ │ ├── costs/ # Cost tracking per provider/model
|
||||
│ │ │ ├── endpoint/ # Unified: Endpoint Proxy, MCP, A2A, API Endpoints tabs
|
||||
│ │ │ ├── health/ # System health (uptime, circuit breakers, latency)
|
||||
│ │ │ ├── limits/ # Rate limits dashboard
|
||||
│ │ │ ├── logs/ # Request logs viewer
|
||||
│ │ │ ├── media/ # Image/video/music generation
|
||||
│ │ │ ├── logs/ # Request, Proxy, Audit, Console logs (tabbed)
|
||||
│ │ │ ├── media/ # Image/video/music generation + transcription
|
||||
│ │ │ ├── playground/ # Model playground (Monaco editor, streaming)
|
||||
│ │ │ ├── providers/ # Provider management (OAuth + API key + free)
|
||||
│ │ │ ├── settings/ # Settings tabs (General, Appearance, Security, Routing, Resilience, Advanced)
|
||||
@@ -51,7 +51,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
│ │ ├── api/ # REST API endpoints
|
||||
│ │ │ ├── v1/ # OpenAI-compatible API (chat, models, embeddings, images, audio)
|
||||
│ │ │ ├── acp/ # ACP agent management API
|
||||
│ │ │ ├── oauth/ # OAuth flows per provider (authorize, exchange, callback)
|
||||
│ │ │ ├── oauth/ # OAuth flows per provider
|
||||
│ │ │ ├── providers/ # Provider CRUD and batch testing
|
||||
│ │ │ ├── models/ # Dashboard model listing and aliases
|
||||
│ │ │ ├── combos/ # Combo CRUD (multi-model fallback chains)
|
||||
@@ -59,131 +59,143 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
│ │ └── login/ # Login page
|
||||
│ ├── domain/ # Domain types and business logic interfaces
|
||||
│ ├── i18n/ # Internationalization
|
||||
│ │ └── messages/ # 30 language JSON files (ar, bg, cs, da, de, en, es, fi, fr, he, hu, id, in, it, ja, ko, ms, nl, no, phi, pl, pt, pt-BR, ro, ru, sk, sv, th, uk-UA, vi, zh-CN)
|
||||
│ │ └── messages/ # 30 language JSON files
|
||||
│ ├── lib/ # Core libraries
|
||||
│ │ ├── acp/ # ACP agent registry and manager (14 built-in agents + custom)
|
||||
│ │ ├── db/ # SQLite database layer (providers, combos, prompts, logs)
|
||||
│ │ ├── a2a/ # Agent-to-Agent v0.3 protocol server
|
||||
│ │ ├── acp/ # ACP agent registry and manager (14 built-in + custom)
|
||||
│ │ ├── db/ # SQLite database layer (core, providers, models, combos, apiKeys, settings, backup)
|
||||
│ │ ├── oauth/ # OAuth providers, services, and utilities
|
||||
│ │ │ ├── providers/ # Provider-specific OAuth configs (GitHub, Google, Claude, etc.)
|
||||
│ │ │ ├── constants/ # Default OAuth credentials (overridable via env)
|
||||
│ │ │ ├── providers/ # Provider-specific OAuth configs
|
||||
│ │ │ ├── services/ # Provider-specific token exchange logic
|
||||
│ │ │ └── utils/ # PKCE, callback server, token helpers
|
||||
│ │ ├── cloudSync.ts # Cloud sync via Cloudflare Workers
|
||||
│ │ ├── tokenHealthCheck.ts # Background OAuth token refresh scheduler
|
||||
│ │ └── localDb.ts # Unified database access layer
|
||||
│ │ └── localDb.ts # Unified re-export layer for all DB modules
|
||||
│ ├── shared/ # Shared utilities, components, and constants
|
||||
│ │ ├── components/ # Reusable UI components (Card, Badge, Button, Modal, Sidebar, etc.)
|
||||
│ │ ├── constants/ # Provider definitions, model lists, pricing
|
||||
│ │ ├── validation/ # Zod schemas (settings, providers, etc.)
|
||||
│ │ ├── components/ # Reusable UI components (Card, Badge, Button, Modal, Sidebar, ProviderIcon, etc.)
|
||||
│ │ ├── constants/ # Provider definitions, model lists, pricing, upstream headers
|
||||
│ │ ├── validation/ # Zod schemas (settings, providers, routes)
|
||||
│ │ └── utils/ # Helpers (auth, CORS, error codes, machine ID)
|
||||
│ ├── sse/ # SSE proxy pipeline
|
||||
│ │ ├── services/ # Auth resolution, format translation, response handling
|
||||
│ │ └── middleware/ # Rate limiting, circuit breaker, caching, idempotency
|
||||
│ ├── store/ # Zustand client-side stores (theme, providers, etc.)
|
||||
│ ├── types/ # TypeScript type definitions
|
||||
│ ├── proxy.ts # Main proxy request handler
|
||||
│ └── server-init.ts # Server initialization (DB, health checks)
|
||||
│ └── types/ # TypeScript type definitions
|
||||
├── open-sse/ # Standalone SSE server (npm workspace)
|
||||
│ ├── config/ # Model registries (embedding, image, audio, rerank, moderation, CLI fingerprints)
|
||||
│ ├── handlers/ # Request handlers per API type
|
||||
│ ├── mcp-server/ # Built-in MCP server (16 tools, audit logging, scope auth)
|
||||
│ └── translators/ # Format translators (OpenAI ↔ Claude ↔ Gemini ↔ Responses ↔ Ollama)
|
||||
├── tests/ # Test suites
|
||||
│ ├── handlers/ # Request handlers per API type (chat, responses, embeddings, images, audio, search)
|
||||
│ ├── mcp-server/ # Built-in MCP server (16 tools, 3 transports: stdio/SSE/streamable-HTTP)
|
||||
│ ├── services/ # Auto-combo engine (6-factor scoring, 4 mode packs, bandit exploration)
|
||||
│ └── translator/ # Format translators (OpenAI ↔ Claude ↔ Gemini ↔ Responses ↔ Ollama ↔ DeepSeek)
|
||||
├── tests/ # Test suites (926 assertions)
|
||||
│ ├── unit/ # Unit tests (32+ test files)
|
||||
│ └── integration/ # Integration tests
|
||||
├── docs/ # Documentation (with 29-language i18n subdirectories)
|
||||
│ ├── i18n/ # Translated docs (ar, bg, cs, da, de, es, fi, fr, he, hu, id, in, it, ja, ko, ms, nl, no, phi, pl, pt, pt-BR, ro, ru, sk, sv, th, uk-UA, vi, zh-CN)
|
||||
├── docs/ # Documentation
|
||||
│ ├── i18n/ # 30-language translated READMEs
|
||||
│ ├── screenshots/ # Dashboard screenshots
|
||||
│ ├── a2a-server.md # A2A agent protocol documentation
|
||||
│ ├── auto-combo.md # Auto-combo engine (6-factor scoring)
|
||||
│ └── mcp-server.md # MCP server (16 tools)
|
||||
├── electron/ # Electron desktop app
|
||||
├── bin/ # CLI entry points (omniroute, reset-password)
|
||||
└── .env.example # Environment variable template
|
||||
```
|
||||
|
||||
## Key Features (v2.0.13)
|
||||
## Key Features (v3.0.0)
|
||||
|
||||
### Core Proxy
|
||||
- **36+ AI providers** with automatic format translation
|
||||
- **67+ AI providers** with automatic format translation
|
||||
- **6 routing strategies**: priority, weighted, round-robin, random, least-used, cost-optimized
|
||||
- **4-tier fallback**: Subscription → API Key → Cheap → Free
|
||||
- **Auto-combo engine**: Self-healing routing optimization with 6-factor scoring, bandit exploration, progressive cooldown
|
||||
- **Semantic caching** with cache hit/miss headers
|
||||
- **Idempotency** with configurable dedup window
|
||||
- **Circuit breaker** per provider with configurable thresholds
|
||||
- **Provider Icons**: 130+ provider logos via `@lobehub/icons` (SVG) with PNG fallback
|
||||
- **Model Auto-Sync**: 24h scheduler refreshes model lists for 16 providers
|
||||
- **Registered Keys API**: Auto-provision API keys via `POST /api/v1/registered-keys` with quota enforcement
|
||||
- **926 tests** with 0 failures
|
||||
|
||||
### Anti-Ban Protection
|
||||
### Security
|
||||
- **CodeQL security**: Fixed 10+ CodeQL alerts (polynomial-redos, insecure-randomness, shell-injection)
|
||||
- **Route validation**: All 176 API routes validated with Zod schemas + `validateBody()`
|
||||
- **omniModel tag sanitization**: Internal `<omniModel>` tags never leak to clients in SSE streams
|
||||
- **TLS Fingerprint Spoofing** — Browser-like TLS fingerprint to reduce bot detection
|
||||
- **CLI Fingerprint Matching** — Per-provider request signature matching (headers/body ordering) to match native CLI tools. Proxy IP is preserved.
|
||||
- **CLI Fingerprint Matching** — Per-provider request signature matching
|
||||
|
||||
### Dashboard Pages
|
||||
- **Providers** — OAuth, API key, and free provider management
|
||||
- **Combos** — Multi-model fallback chain builder with templates
|
||||
- **Providers** — OAuth, API key, and free provider management with ProviderIcon SVG icons
|
||||
- **Combos** — Multi-model combo builder with 4 templates (Free Stack, High Availability, Cost Saver, Balanced) + 9 strategies
|
||||
- **Analytics** — Token consumption, cost, heatmaps, distributions
|
||||
- **Health** — Uptime, memory, latency percentiles, circuit breakers
|
||||
- **Logs** — Real-time request log viewer with filtering
|
||||
- **Logs** — Request, Proxy, Audit, Console (tabbed)
|
||||
- **Costs** — Cost tracking per provider/model
|
||||
- **Limits** — Rate limit monitoring
|
||||
- **CLI Tools** — One-click configuration for 10+ AI CLI tools
|
||||
- **CLI Agents** — Grid of 14 built-in agents with install detection + custom agent registration
|
||||
- **CLI Agents** — Grid of 14+ built-in agents with ProviderIcon and install detection + custom agent registration
|
||||
- **Playground** — Test any model with Monaco editor, streaming responses
|
||||
- **Media** — Image/video/music generation (DALL-E, FLUX, AnimateDiff, etc.)
|
||||
- **Media** — Image/video/music generation (DALL-E, FLUX, etc.) + audio transcription (up to 2GB files)
|
||||
- **Translator** — Format debugging: playground, chat tester, test bench, live monitor
|
||||
- **Settings** — General, Appearance (7 color themes), Security (TLS/CLI fingerprint, IP filter), Routing, Resilience, Advanced
|
||||
- **Endpoint** — Unified API endpoint info + cloud proxy
|
||||
|
||||
### Sidebar Organization
|
||||
- **Main**: Home, Endpoints, API Manager, Providers, Combos, Costs, Analytics, Limits
|
||||
- **CLI**: Tools, Agents
|
||||
- **Debug**: Translator, Playground, Media
|
||||
- **System**: Health, Logs, Settings
|
||||
- **Help**: Docs, Issues
|
||||
- **Endpoint** — Unified: Endpoint Proxy, MCP Server, A2A Server, API Endpoints (tabbed)
|
||||
|
||||
### Protocol Support
|
||||
- **OpenAI-compatible** — `/v1/chat/completions`, `/v1/models`, `/v1/embeddings`, `/v1/images/generations`, `/v1/audio/transcriptions`
|
||||
- **OpenAI-compatible** — `/v1/chat/completions`, `/v1/models`, `/v1/embeddings`, `/v1/images/generations`, `/v1/audio/transcriptions`, `/v1/audio/speech`
|
||||
- **Anthropic** — `/v1/messages`, `/v1/messages/count_tokens`
|
||||
- **OpenAI Responses** — `/v1/responses`
|
||||
- **Gemini** — `/v1beta/models`, `/v1beta/models/{...path}`
|
||||
- **Ollama** — `/v1/api/chat`, `/api/tags`
|
||||
- **MCP** — 16-tool MCP server with scope-based auth
|
||||
- **A2A** — Agent-to-Agent protocol (smart-routing, quota-management skills)
|
||||
- **MCP** — 16-tool MCP server with scope-based auth (3 transports: stdio, SSE, streamable HTTP)
|
||||
- **A2A** — Agent-to-Agent v0.3 protocol (JSON-RPC 2.0, smart-routing + quota-management skills)
|
||||
- **ACP** — Agent detection, custom agent registry
|
||||
|
||||
### MCP Server (16 Tools)
|
||||
| Category | Tools |
|
||||
|-----------|-------|
|
||||
| Essential | `get_health`, `list_combos`, `get_combo_metrics`, `switch_combo`, `check_quota`, `route_request`, `cost_report`, `list_models_catalog` |
|
||||
| Advanced | `simulate_route`, `set_budget_guard`, `set_resilience_profile`, `test_combo`, `get_provider_metrics`, `best_combo_for_task`, `explain_route`, `get_session_snapshot` |
|
||||
|
||||
### Internationalization
|
||||
- 30 languages for UI (sidebar, settings, agents, and all dashboard pages)
|
||||
- 30 READMEs (root README.md + 29 translated README.*.md)
|
||||
- 29 translated doc sets in docs/i18n/
|
||||
- 30 languages for UI (all dashboard pages)
|
||||
- 30 translated READMEs in docs/i18n/
|
||||
- Language switcher in documentation
|
||||
|
||||
## Key Architectural Decisions
|
||||
|
||||
1. **OpenAI-compatible API surface:** All incoming requests follow the OpenAI API format (`/v1/chat/completions`, `/v1/models`, etc.). This makes OmniRoute a drop-in replacement for any tool that supports custom OpenAI endpoints.
|
||||
1. **OpenAI-compatible API surface:** All incoming requests follow the OpenAI API format. This makes OmniRoute a drop-in replacement for any tool that supports custom OpenAI endpoints.
|
||||
|
||||
2. **Provider abstraction via format translators:** Each AI provider (Claude, Gemini, etc.) has a translator in `open-sse/translators/` that converts between the OpenAI format and the provider's native format. This happens transparently.
|
||||
2. **Provider abstraction via format translators:** Each AI provider has a translator in `open-sse/translator/` that converts between OpenAI format and the provider's native format transparently.
|
||||
|
||||
3. **Connection-based provider model:** Providers are stored as "connections" in SQLite. Each connection has an `id`, `provider`, `authType` (oauth/apikey/free), `isActive` flag, and credentials. Multiple connections per provider are supported for multi-account rotation.
|
||||
3. **Connection-based provider model:** Providers are stored as "connections" in SQLite. Each connection has an `id`, `provider`, `authType` (oauth/apikey/free), `isActive` flag, and credentials. Multiple connections per provider for multi-account rotation.
|
||||
|
||||
4. **Combo system for fallback:** Users create "combos" — ordered lists of `provider/model` pairs. The proxy tries each in order until one succeeds. Supports 6 strategies.
|
||||
4. **Combo system for fallback:** Users create "combos" — ordered lists of `provider/model` pairs. The proxy tries each in order until one succeeds. Supports 9 strategies including auto-combo with self-healing.
|
||||
|
||||
5. **SSE proxy pipeline (`src/sse/`):** The proxy pipeline is middleware-based: request → auth resolution → rate limiting → circuit breaker → format translation → upstream call → response translation → SSE streaming back to client.
|
||||
5. **SSE proxy pipeline:** The proxy pipeline is middleware-based: request → auth resolution → rate limiting → circuit breaker → format translation → upstream call → response translation → SSE streaming back to client.
|
||||
|
||||
6. **SQLite for persistence:** All state (providers, combos, logs, settings) is stored in a single SQLite database file at `data/omniroute.db`. This keeps the app self-contained and zero-config.
|
||||
6. **SQLite for persistence:** All state (providers, combos, logs, settings, API keys) stored in a single SQLite database. All DB operations go through `src/lib/db/` modules, never raw SQL in routes.
|
||||
|
||||
7. **OAuth with PKCE:** OAuth flows use PKCE for security. A local callback server handles the redirect. Token refresh is handled by a background job (`tokenHealthCheck.ts`).
|
||||
7. **OAuth with PKCE:** OAuth flows use PKCE for security. Token refresh handled by background job (`tokenHealthCheck.ts`).
|
||||
|
||||
8. **ACP Agent Registry:** 14 built-in CLI agents with dynamic detection and a 60-second cache. Custom agents can be added via dashboard or API, stored in settings DB.
|
||||
8. **ProviderIcon component:** Unified icon system using `@lobehub/icons` (130+ SVG) with PNG fallback and generic icon fallback chain. Used on providers, dashboard, and agents pages.
|
||||
|
||||
9. **DB architecture:** `localDb.ts` is a re-export layer only — real logic lives in `src/lib/db/` modules (core, providers, models, combos, apiKeys, settings, backup).
|
||||
|
||||
10. **Upstream headers:** Custom headers merged in executors after default auth; same header name replaces executor value. Forbidden header names in `src/shared/constants/upstreamHeaders.ts`.
|
||||
|
||||
## Main Flows
|
||||
|
||||
### Proxy Request Flow
|
||||
1. Client sends OpenAI-format request to `/v1/chat/completions`
|
||||
2. API key validation (`src/shared/utils/apiAuth.ts`)
|
||||
2. API key validation
|
||||
3. Model resolution: direct model or combo lookup
|
||||
4. For combos: iterate through models in fallback order
|
||||
4. For combos: iterate through models with selected strategy
|
||||
5. Auth resolution: get credentials for the target provider
|
||||
6. Format translation: OpenAI → provider native format
|
||||
7. CLI fingerprint matching (if enabled for provider)
|
||||
8. Upstream request with circuit breaker and rate limiting
|
||||
9. Response translation: provider → OpenAI format
|
||||
10. SSE streaming back to client
|
||||
10. omniModel tag sanitization (strip internal tags)
|
||||
11. SSE streaming back to client
|
||||
|
||||
### OAuth Flow
|
||||
1. Dashboard initiates `/api/oauth/[provider]/authorize`
|
||||
@@ -192,31 +204,27 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
4. Tokens stored as a provider connection in SQLite
|
||||
5. Background job refreshes tokens before expiry
|
||||
|
||||
### Model Listing
|
||||
- `/api/models` — Dashboard endpoint, lists all defined models with aliases
|
||||
- `/v1/models` — OpenAI-compatible endpoint, lists only models from active providers
|
||||
|
||||
## Important Notes for LLMs
|
||||
|
||||
1. **Two model endpoints exist:** `/api/models` (dashboard, all models) and `/v1/models` (OpenAI-compatible, active only). Don't confuse them.
|
||||
1. **Two model endpoints exist:** `/api/models` (dashboard, all models) and `/v1/models` (OpenAI-compatible, active only).
|
||||
|
||||
2. **Provider IDs vs aliases:** Providers have both an ID (`claude`, `github`) and a short alias (`cc`, `gh`). Models are referenced as `alias/model-name` (e.g., `cc/claude-opus-4-6`).
|
||||
|
||||
3. **The `open-sse/` directory is a separate npm workspace** with its own config, handlers, and translators. It handles the actual SSE streaming and format translation.
|
||||
3. **The `open-sse/` directory is a separate npm workspace** with its own config, handlers, and translators.
|
||||
|
||||
4. **Environment variables:** All configuration is in `.env` (from `.env.example`). Key vars: `PORT`, `NEXT_PUBLIC_BASE_URL`, `API_KEY`, `ADMIN_PASSWORD`.
|
||||
|
||||
5. **Database migrations:** SQLite schema is managed inline in `src/lib/db/core.ts` and `src/lib/db/providers.ts`. No migration framework — schema changes are applied on startup.
|
||||
5. **Database layer:** Operations go through `src/lib/db/` modules. `localDb.ts` is re-exports only — add new functions to the proper `db/*.ts` module.
|
||||
|
||||
6. **Tests use Node.js built-in test runner:** Run `npm test` or `node --test tests/unit/*.test.mjs`. Playwright is used for E2E tests.
|
||||
6. **Tests use Node.js built-in test runner:** 926 assertions across 32+ test files. Run `npm test`.
|
||||
|
||||
7. **The proxy pipeline is in `src/sse/`**, not in `src/app/api/v1/`. The API routes in `src/app/api/v1/` delegate to the SSE server running on a separate Express instance.
|
||||
7. **MCP and A2A pages are embedded as tabs inside `/dashboard/endpoint`**, not standalone routes.
|
||||
|
||||
8. **Sidebar sections:** Main nav, CLI (Tools + Agents), Debug (Translator + Playground + Media), System (Health + Logs + Settings), Help (Docs + Issues).
|
||||
8. **ACP agents** are in `src/lib/acp/registry.ts` (14 built-in) with a 60s detection cache. Custom agents stored via settings DB.
|
||||
|
||||
9. **ACP agents** are in `src/lib/acp/registry.ts` (14 built-in) with a 60s detection cache. Custom agents stored via `src/shared/validation/settingsSchemas.ts`.
|
||||
9. **Auto-combo engine** in `open-sse/services/autoCombo/` — 6-factor scoring, 4 mode packs, bandit exploration, progressive cooldown.
|
||||
|
||||
10. **CLI fingerprint configs** are in `open-sse/config/cliFingerprints.ts`. They match native CLI request patterns per provider.
|
||||
10. **Docker:** Dockerfile has two targets: `runner-base` and `runner-cli`. `docker-compose.yml` for dev (3 profiles), `docker-compose.prod.yml` for production (port 20130).
|
||||
|
||||
## Links
|
||||
|
||||
|
||||
@@ -26,9 +26,18 @@ const CONFIG_TTL_MS = 60_000;
|
||||
let lastLoadTime = 0;
|
||||
let cachedProviders = null;
|
||||
|
||||
// Survives Next.js dev HMR: module-level cache resets but process is the same (V4 pattern).
|
||||
type CredGlobals = typeof globalThis & { __omnirouteCredNoFileLogged?: boolean };
|
||||
function credGlobals(): CredGlobals {
|
||||
return globalThis as CredGlobals;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the path to provider-credentials.json
|
||||
* Priority: DATA_DIR env → ./data (project root)
|
||||
* Resolves the path to provider-credentials.json using the application's
|
||||
* data directory. Delegates to resolveDataDir() which handles DATA_DIR env,
|
||||
* platform-specific defaults, and fallback logic.
|
||||
*
|
||||
* previous: Priority: DATA_DIR env → ./data (project root)
|
||||
*/
|
||||
function resolveCredentialsPath() {
|
||||
return join(resolveDataDir(), "provider-credentials.json");
|
||||
@@ -51,8 +60,9 @@ export function loadProviderCredentials(providers) {
|
||||
const credPath = resolveCredentialsPath();
|
||||
|
||||
if (!existsSync(credPath)) {
|
||||
if (!cachedProviders) {
|
||||
if (!credGlobals().__omnirouteCredNoFileLogged) {
|
||||
console.log("[CREDENTIALS] No external credentials file found, using defaults.");
|
||||
credGlobals().__omnirouteCredNoFileLogged = true;
|
||||
}
|
||||
cachedProviders = providers;
|
||||
lastLoadTime = Date.now();
|
||||
|
||||
@@ -16,6 +16,8 @@ export interface RegistryModel {
|
||||
toolCalling?: boolean;
|
||||
targetFormat?: string;
|
||||
unsupportedParams?: readonly string[];
|
||||
/** Maximum context window in tokens */
|
||||
contextLength?: number;
|
||||
}
|
||||
|
||||
// Reasoning models reject temperature, top_p, penalties, logprobs, n.
|
||||
@@ -65,6 +67,8 @@ export interface RegistryEntry {
|
||||
chatPath?: string;
|
||||
clientVersion?: string;
|
||||
passthroughModels?: boolean;
|
||||
/** Default context window for all models in this provider (can be overridden per-model) */
|
||||
defaultContextLength?: number;
|
||||
}
|
||||
|
||||
interface LegacyProvider {
|
||||
@@ -137,6 +141,7 @@ export const REGISTRY: Record<string, RegistryEntry> = {
|
||||
urlSuffix: "?beta=true",
|
||||
authType: "oauth",
|
||||
authHeader: "x-api-key",
|
||||
defaultContextLength: 200000,
|
||||
headers: {
|
||||
"Anthropic-Version": "2023-06-01",
|
||||
"Anthropic-Beta":
|
||||
@@ -180,6 +185,7 @@ export const REGISTRY: Record<string, RegistryEntry> = {
|
||||
},
|
||||
authType: "apikey",
|
||||
authHeader: "x-goog-api-key",
|
||||
defaultContextLength: 1000000,
|
||||
oauth: {
|
||||
clientIdEnv: "GEMINI_OAUTH_CLIENT_ID",
|
||||
clientIdDefault: "681255809395-oo8ft2oprdrnp9e3aqf6av3hmdib135j.apps.googleusercontent.com",
|
||||
@@ -216,6 +222,7 @@ export const REGISTRY: Record<string, RegistryEntry> = {
|
||||
},
|
||||
authType: "oauth",
|
||||
authHeader: "bearer",
|
||||
defaultContextLength: 1000000,
|
||||
oauth: {
|
||||
clientIdEnv: "GEMINI_CLI_OAUTH_CLIENT_ID",
|
||||
clientIdDefault: "681255809395-oo8ft2oprdrnp9e3aqf6av3hmdib135j.apps.googleusercontent.com",
|
||||
@@ -247,6 +254,7 @@ export const REGISTRY: Record<string, RegistryEntry> = {
|
||||
baseUrl: "https://chatgpt.com/backend-api/codex/responses",
|
||||
authType: "oauth",
|
||||
authHeader: "bearer",
|
||||
defaultContextLength: 400000,
|
||||
headers: {
|
||||
Version: "0.92.0",
|
||||
"Openai-Beta": "responses=experimental",
|
||||
@@ -399,6 +407,7 @@ export const REGISTRY: Record<string, RegistryEntry> = {
|
||||
responsesBaseUrl: "https://api.githubcopilot.com/responses",
|
||||
authType: "oauth",
|
||||
authHeader: "bearer",
|
||||
defaultContextLength: 128000,
|
||||
headers: {
|
||||
"copilot-integration-id": "vscode-chat",
|
||||
"editor-version": "vscode/1.110.0",
|
||||
@@ -447,6 +456,7 @@ export const REGISTRY: Record<string, RegistryEntry> = {
|
||||
baseUrl: "https://codewhisperer.us-east-1.amazonaws.com/generateAssistantResponse",
|
||||
authType: "oauth",
|
||||
authHeader: "bearer",
|
||||
defaultContextLength: 200000,
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Accept: "application/vnd.amazon.eventstream",
|
||||
@@ -473,6 +483,7 @@ export const REGISTRY: Record<string, RegistryEntry> = {
|
||||
chatPath: "/aiserver.v1.ChatService/StreamUnifiedChatWithTools",
|
||||
authType: "oauth",
|
||||
authHeader: "bearer",
|
||||
defaultContextLength: 200000,
|
||||
headers: {
|
||||
"connect-accept-encoding": "gzip",
|
||||
"connect-protocol-version": "1",
|
||||
@@ -501,6 +512,7 @@ export const REGISTRY: Record<string, RegistryEntry> = {
|
||||
baseUrl: "https://api.openai.com/v1/chat/completions",
|
||||
authType: "apikey",
|
||||
authHeader: "bearer",
|
||||
defaultContextLength: 128000,
|
||||
models: [
|
||||
{ id: "gpt-4o", name: "GPT-4o" },
|
||||
{ id: "gpt-4o-mini", name: "GPT-4o Mini" },
|
||||
@@ -522,6 +534,7 @@ export const REGISTRY: Record<string, RegistryEntry> = {
|
||||
urlSuffix: "?beta=true",
|
||||
authType: "apikey",
|
||||
authHeader: "x-api-key",
|
||||
defaultContextLength: 200000,
|
||||
headers: {
|
||||
"Anthropic-Version": "2023-06-01",
|
||||
},
|
||||
@@ -548,6 +561,7 @@ export const REGISTRY: Record<string, RegistryEntry> = {
|
||||
authType: "apikey",
|
||||
authHeader: "Authorization",
|
||||
authPrefix: "Bearer",
|
||||
defaultContextLength: 200000,
|
||||
models: [
|
||||
{ id: "glm-5", name: "GLM-5" },
|
||||
{ id: "kimi-k2.5", name: "Kimi K2.5" },
|
||||
@@ -565,6 +579,7 @@ export const REGISTRY: Record<string, RegistryEntry> = {
|
||||
authType: "apikey",
|
||||
authHeader: "Authorization",
|
||||
authPrefix: "Bearer",
|
||||
defaultContextLength: 200000,
|
||||
models: [
|
||||
{ id: "minimax-m2.5-free", name: "MiniMax M2.5 Free" },
|
||||
{ id: "big-pickle", name: "Big Pickle" },
|
||||
@@ -580,6 +595,7 @@ export const REGISTRY: Record<string, RegistryEntry> = {
|
||||
baseUrl: "https://openrouter.ai/api/v1/chat/completions",
|
||||
authType: "apikey",
|
||||
authHeader: "bearer",
|
||||
defaultContextLength: 128000,
|
||||
headers: {
|
||||
"HTTP-Referer": "https://endpoint-proxy.local",
|
||||
"X-Title": "Endpoint Proxy",
|
||||
@@ -593,6 +609,7 @@ export const REGISTRY: Record<string, RegistryEntry> = {
|
||||
format: "claude",
|
||||
executor: "default",
|
||||
baseUrl: "https://api.z.ai/api/anthropic/v1/messages",
|
||||
defaultContextLength: 200000,
|
||||
urlSuffix: "?beta=true",
|
||||
authType: "apikey",
|
||||
authHeader: "x-api-key",
|
||||
@@ -1353,7 +1370,7 @@ export const REGISTRY: Record<string, RegistryEntry> = {
|
||||
{ id: "qwen/qwen3-32b", name: "Qwen3 32B (Puter)" },
|
||||
{ id: "qwen/qwen3-coder", name: "Qwen3 Coder 480B (Puter)" },
|
||||
],
|
||||
passthroughModels: true, // 500+ models available — users can type any Puter model ID
|
||||
passthroughModels: true, // 500+ models available — users can type arbitrary Puter model IDs
|
||||
},
|
||||
|
||||
"cloudflare-ai": {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import crypto from "crypto";
|
||||
import { BaseExecutor } from "./base.ts";
|
||||
import { BaseExecutor, mergeUpstreamExtraHeaders } from "./base.ts";
|
||||
import { PROVIDERS, OAUTH_ENDPOINTS, HTTP_STATUS } from "../config/constants.ts";
|
||||
|
||||
const MAX_RETRY_AFTER_MS = 10000;
|
||||
@@ -198,7 +198,7 @@ export class AntigravityExecutor extends BaseExecutor {
|
||||
return totalMs > 0 ? totalMs : null;
|
||||
}
|
||||
|
||||
async execute({ model, body, stream, credentials, signal, log }) {
|
||||
async execute({ model, body, stream, credentials, signal, log, upstreamExtraHeaders }) {
|
||||
const fallbackCount = this.getFallbackCount();
|
||||
let lastError = null;
|
||||
let lastStatus = 0;
|
||||
@@ -208,6 +208,7 @@ export class AntigravityExecutor extends BaseExecutor {
|
||||
for (let urlIndex = 0; urlIndex < fallbackCount; urlIndex++) {
|
||||
const url = this.buildUrl(model, stream, urlIndex);
|
||||
const headers = this.buildHeaders(credentials, stream);
|
||||
mergeUpstreamExtraHeaders(headers, upstreamExtraHeaders);
|
||||
const transformedBody = this.transformRequest(model, body, stream, credentials);
|
||||
|
||||
// Initialize retry counter for this URL
|
||||
|
||||
@@ -58,8 +58,23 @@ export type ExecuteInput = {
|
||||
signal?: AbortSignal | null;
|
||||
log?: ExecutorLog | null;
|
||||
extendedContext?: boolean;
|
||||
/** Merged after auth + CLI fingerprint headers (values override same-named defaults). */
|
||||
upstreamExtraHeaders?: Record<string, string> | null;
|
||||
};
|
||||
|
||||
/** Apply model-level extra upstream headers (e.g. Authentication, X-Custom-Auth). */
|
||||
export function mergeUpstreamExtraHeaders(
|
||||
headers: Record<string, string>,
|
||||
extra?: Record<string, string> | null
|
||||
): void {
|
||||
if (!extra) return;
|
||||
for (const [k, v] of Object.entries(extra)) {
|
||||
if (typeof k === "string" && k.length > 0 && typeof v === "string") {
|
||||
headers[k] = v;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function mergeAbortSignals(primary: AbortSignal, secondary: AbortSignal): AbortSignal {
|
||||
const controller = new AbortController();
|
||||
|
||||
@@ -204,7 +219,16 @@ export class BaseExecutor {
|
||||
return { status: response.status, message: bodyText || `HTTP ${response.status}` };
|
||||
}
|
||||
|
||||
async execute({ model, body, stream, credentials, signal, log, extendedContext }: ExecuteInput) {
|
||||
async execute({
|
||||
model,
|
||||
body,
|
||||
stream,
|
||||
credentials,
|
||||
signal,
|
||||
log,
|
||||
extendedContext,
|
||||
upstreamExtraHeaders,
|
||||
}: ExecuteInput) {
|
||||
const fallbackCount = this.getFallbackCount();
|
||||
let lastError: unknown = null;
|
||||
let lastStatus = 0;
|
||||
@@ -258,6 +282,8 @@ export class BaseExecutor {
|
||||
bodyString = fingerprinted.bodyString;
|
||||
}
|
||||
|
||||
mergeUpstreamExtraHeaders(finalHeaders, upstreamExtraHeaders);
|
||||
|
||||
const fetchOptions: RequestInit = {
|
||||
method: "POST",
|
||||
headers: finalHeaders,
|
||||
@@ -289,7 +315,7 @@ export class BaseExecutor {
|
||||
continue;
|
||||
}
|
||||
|
||||
return { response, url, headers, transformedBody };
|
||||
return { response, url, headers: finalHeaders, transformedBody };
|
||||
} catch (error) {
|
||||
// Distinguish timeout errors from other abort errors
|
||||
const err = error instanceof Error ? error : new Error(String(error));
|
||||
|
||||
@@ -20,7 +20,7 @@ declare const EdgeRuntime: string | undefined;
|
||||
* @see cursorProtobuf.js for Protobuf encoding/decoding utilities
|
||||
*/
|
||||
|
||||
import { BaseExecutor } from "./base.ts";
|
||||
import { BaseExecutor, mergeUpstreamExtraHeaders } from "./base.ts";
|
||||
import { PROVIDERS, HTTP_STATUS } from "../config/constants.ts";
|
||||
import {
|
||||
generateCursorBody,
|
||||
@@ -363,9 +363,10 @@ export class CursorExecutor extends BaseExecutor {
|
||||
});
|
||||
}
|
||||
|
||||
async execute({ model, body, stream, credentials, signal, log }) {
|
||||
async execute({ model, body, stream, credentials, signal, log, upstreamExtraHeaders }) {
|
||||
const url = this.buildUrl();
|
||||
const headers = this.buildHeaders(credentials);
|
||||
mergeUpstreamExtraHeaders(headers, upstreamExtraHeaders);
|
||||
const transformedBody = this.transformRequest(model, body, stream, credentials);
|
||||
|
||||
try {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import {
|
||||
BaseExecutor,
|
||||
mergeUpstreamExtraHeaders,
|
||||
type ExecuteInput,
|
||||
type ExecutorLog,
|
||||
type ProviderCredentials,
|
||||
@@ -89,9 +90,18 @@ export class KiroExecutor extends BaseExecutor {
|
||||
/**
|
||||
* Custom execute for Kiro - handles AWS EventStream binary response
|
||||
*/
|
||||
async execute({ model, body, stream, credentials, signal, log }: ExecuteInput) {
|
||||
async execute({
|
||||
model,
|
||||
body,
|
||||
stream,
|
||||
credentials,
|
||||
signal,
|
||||
log,
|
||||
upstreamExtraHeaders,
|
||||
}: ExecuteInput) {
|
||||
const url = this.buildUrl(model, stream, 0);
|
||||
const headers = this.buildHeaders(credentials, stream);
|
||||
mergeUpstreamExtraHeaders(headers, upstreamExtraHeaders);
|
||||
const transformedBody = this.transformRequest(model, body, stream, credentials);
|
||||
|
||||
const response = await fetch(url, {
|
||||
|
||||
@@ -26,7 +26,11 @@ import {
|
||||
appendRequestLog,
|
||||
saveCallLog,
|
||||
} from "@/lib/usageDb";
|
||||
import { getModelNormalizeToolCallId, getModelPreserveOpenAIDeveloperRole } from "@/lib/localDb";
|
||||
import {
|
||||
getModelNormalizeToolCallId,
|
||||
getModelPreserveOpenAIDeveloperRole,
|
||||
getModelUpstreamExtraHeaders,
|
||||
} from "@/lib/localDb";
|
||||
import { getExecutor } from "../executors/index.ts";
|
||||
import {
|
||||
parseCodexQuotaHeaders,
|
||||
@@ -76,7 +80,8 @@ export function shouldUseNativeCodexPassthrough({
|
||||
if (provider !== "codex") return false;
|
||||
if (sourceFormat !== FORMATS.OPENAI_RESPONSES) return false;
|
||||
const normalizedEndpoint = String(endpointPath || "").replace(/\/+$/, "");
|
||||
return /(?:^|\/)responses(?:\/.*)?$/i.test(normalizedEndpoint);
|
||||
const segments = normalizedEndpoint.split("/");
|
||||
return segments.includes("responses");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -218,7 +223,8 @@ export async function handleChatCore({
|
||||
|
||||
const endpointPath = String(clientRawRequest?.endpoint || "");
|
||||
const sourceFormat = detectFormatFromEndpoint(body, endpointPath);
|
||||
const isResponsesEndpoint = /(?:^|\/)responses(?:\/.*)?$/i.test(endpointPath);
|
||||
const isResponsesEndpoint =
|
||||
/\/responses(?=\/|$)/i.test(endpointPath) || /^responses(?=\/|$)/i.test(endpointPath);
|
||||
const nativeCodexPassthrough = shouldUseNativeCodexPassthrough({
|
||||
provider,
|
||||
sourceFormat,
|
||||
@@ -280,6 +286,23 @@ export async function handleChatCore({
|
||||
const modelTargetFormat = getModelTargetFormat(alias, resolvedModel);
|
||||
const targetFormat = modelTargetFormat || getTargetFormat(provider);
|
||||
|
||||
// Primary path: merge client model id + alias target so config on either key applies; resolved
|
||||
// id wins on same header name. T5 family fallback uses only (nextModel, resolveModelAlias(next))
|
||||
// so A-model headers are not sent to B — see buildUpstreamHeadersForExecute.
|
||||
const buildUpstreamHeadersForExecute = (modelToCall: string): Record<string, string> => {
|
||||
if (modelToCall === effectiveModel) {
|
||||
return {
|
||||
...getModelUpstreamExtraHeaders(provider || "", model || "", sourceFormat),
|
||||
...getModelUpstreamExtraHeaders(provider || "", resolvedModel || "", sourceFormat),
|
||||
};
|
||||
}
|
||||
const r = resolveModelAlias(modelToCall);
|
||||
return {
|
||||
...getModelUpstreamExtraHeaders(provider || "", modelToCall || "", sourceFormat),
|
||||
...getModelUpstreamExtraHeaders(provider || "", r || "", sourceFormat),
|
||||
};
|
||||
};
|
||||
|
||||
// Default to false unless client explicitly sets stream: true (OpenAI spec compliant)
|
||||
const acceptHeader =
|
||||
clientRawRequest?.headers && typeof clientRawRequest.headers.get === "function"
|
||||
@@ -593,6 +616,7 @@ export async function handleChatCore({
|
||||
signal: streamController.signal,
|
||||
log,
|
||||
extendedContext,
|
||||
upstreamExtraHeaders: buildUpstreamHeadersForExecute(modelToCall),
|
||||
})
|
||||
);
|
||||
|
||||
@@ -724,16 +748,19 @@ export async function handleChatCore({
|
||||
await onCredentialsRefreshed(newCredentials);
|
||||
}
|
||||
|
||||
// Retry with new credentials
|
||||
// Retry with new credentials — model + extra headers follow translatedBody.model so they
|
||||
// stay aligned if this block ever runs after a path that mutates body.model (e.g. fallback).
|
||||
try {
|
||||
const retryModelId = String(translatedBody.model || effectiveModel);
|
||||
const retryResult = await executor.execute({
|
||||
model,
|
||||
model: retryModelId,
|
||||
body: translatedBody,
|
||||
stream,
|
||||
credentials: getExecutionCredentials(),
|
||||
signal: streamController.signal,
|
||||
log,
|
||||
extendedContext,
|
||||
upstreamExtraHeaders: buildUpstreamHeadersForExecute(retryModelId),
|
||||
});
|
||||
|
||||
if (retryResult.response.ok) {
|
||||
@@ -1041,8 +1068,14 @@ export async function handleChatCore({
|
||||
}
|
||||
|
||||
// Translate response to client's expected format (usually OpenAI)
|
||||
// Pass toolNameMap so Claude OAuth proxy_ prefix is stripped in tool_use blocks (#605)
|
||||
let translatedResponse = needsTranslation(targetFormat, sourceFormat)
|
||||
? translateNonStreamingResponse(responseBody, targetFormat, sourceFormat)
|
||||
? translateNonStreamingResponse(
|
||||
responseBody,
|
||||
targetFormat,
|
||||
sourceFormat,
|
||||
toolNameMap as Map<string, string> | null
|
||||
)
|
||||
: responseBody;
|
||||
|
||||
// T26: Strip markdown code blocks if provider format is Claude
|
||||
|
||||
@@ -1081,6 +1081,21 @@ async function handleComfyUIImageGeneration({ model, provider, providerConfig, b
|
||||
}
|
||||
}
|
||||
|
||||
type Imagen3ImageGenArgs = {
|
||||
model: string;
|
||||
provider: string;
|
||||
providerConfig: { baseUrl: string };
|
||||
body: { prompt?: string; size?: string; n?: number };
|
||||
credentials: { apiKey?: string; accessToken?: string };
|
||||
log?: { info?: (tag: string, msg: string) => void; error?: (tag: string, msg: string) => void } | null;
|
||||
};
|
||||
|
||||
type Imagen3NormalizedImage = {
|
||||
b64_json?: unknown;
|
||||
url?: unknown;
|
||||
revised_prompt?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle Imagen 3 image generation
|
||||
*/
|
||||
@@ -1091,7 +1106,7 @@ async function handleImagen3ImageGeneration({
|
||||
body,
|
||||
credentials,
|
||||
log,
|
||||
}: any) {
|
||||
}: Imagen3ImageGenArgs) {
|
||||
const startTime = Date.now();
|
||||
const token = credentials.apiKey || credentials.accessToken;
|
||||
const aspectRatio = mapImageSize(body.size);
|
||||
@@ -1142,11 +1157,11 @@ async function handleImagen3ImageGeneration({
|
||||
const data = await response.json();
|
||||
|
||||
// Normalize response to OpenAI format
|
||||
const images: any[] = [];
|
||||
const images: Imagen3NormalizedImage[] = [];
|
||||
if (Array.isArray(data.images)) {
|
||||
images.push(
|
||||
...data.images.map((img: any) => ({
|
||||
b64_json: img.image || img.b64_json || img.url || img,
|
||||
...data.images.map((img: Record<string, unknown>) => ({
|
||||
b64_json: img.image ?? img.b64_json ?? img.url ?? img,
|
||||
revised_prompt: body.prompt,
|
||||
}))
|
||||
);
|
||||
@@ -1174,8 +1189,9 @@ async function handleImagen3ImageGeneration({
|
||||
success: true,
|
||||
data: { created: data.created || Math.floor(Date.now() / 1000), data: images },
|
||||
};
|
||||
} catch (err: any) {
|
||||
if (log) log.error("IMAGE", `${provider} fetch error: ${err.message}`);
|
||||
} catch (err: unknown) {
|
||||
const errMsg = err instanceof Error ? err.message : String(err);
|
||||
if (log) log.error("IMAGE", `${provider} fetch error: ${errMsg}`);
|
||||
|
||||
saveCallLog({
|
||||
method: "POST",
|
||||
@@ -1184,9 +1200,9 @@ async function handleImagen3ImageGeneration({
|
||||
model: `${provider}/${model}`,
|
||||
provider,
|
||||
duration: Date.now() - startTime,
|
||||
error: err.message,
|
||||
error: errMsg,
|
||||
}).catch(() => {});
|
||||
|
||||
return { success: false, status: 502, error: `Image provider error: ${err.message}` };
|
||||
return { success: false, status: 502, error: `Image provider error: ${errMsg}` };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,11 +68,14 @@ function findBestMessageText(output: unknown[]): {
|
||||
/**
|
||||
* Translate non-streaming response to OpenAI format
|
||||
* Handles different provider response formats (Gemini, Claude, etc.)
|
||||
*
|
||||
* @param toolNameMap - Optional Map<prefixedName, originalName> for Claude OAuth tool name stripping
|
||||
*/
|
||||
export function translateNonStreamingResponse(
|
||||
responseBody: unknown,
|
||||
targetFormat: string,
|
||||
sourceFormat: string
|
||||
sourceFormat: string,
|
||||
toolNameMap?: Map<string, string> | null
|
||||
): unknown {
|
||||
// If already in source format (usually OpenAI), return as-is
|
||||
if (targetFormat === sourceFormat || targetFormat === FORMATS.OPENAI) {
|
||||
@@ -122,11 +125,14 @@ export function translateNonStreamingResponse(
|
||||
typeof itemObj.arguments === "string"
|
||||
? itemObj.arguments
|
||||
: JSON.stringify(itemObj.arguments || {});
|
||||
const rawName = toString(itemObj.name);
|
||||
// Strip Claude OAuth proxy_ prefix using toolNameMap (mirrors tool_use fix for #605)
|
||||
const resolvedName = toolNameMap?.get(rawName) ?? rawName;
|
||||
toolCalls.push({
|
||||
id: callId,
|
||||
type: "function",
|
||||
function: {
|
||||
name: toString(itemObj.name),
|
||||
name: resolvedName,
|
||||
arguments: fnArgs,
|
||||
},
|
||||
});
|
||||
@@ -334,11 +340,15 @@ export function translateNonStreamingResponse(
|
||||
} else if (blockObj.type === "thinking") {
|
||||
thinkingContent += toString(blockObj.thinking);
|
||||
} else if (blockObj.type === "tool_use") {
|
||||
// Strip Claude OAuth tool name prefix (proxy_) using the map from request translation.
|
||||
// Fallback to raw name if block wasn't prefixed (disableToolPrefix path).
|
||||
const rawName = toString(blockObj.name);
|
||||
const strippedName = toolNameMap?.get(rawName) ?? rawName;
|
||||
toolCalls.push({
|
||||
id: toString(blockObj.id, `call_${Date.now()}_${toolCalls.length}`),
|
||||
type: "function",
|
||||
function: {
|
||||
name: toString(blockObj.name),
|
||||
name: strippedName,
|
||||
arguments: JSON.stringify(blockObj.input || {}),
|
||||
},
|
||||
});
|
||||
|
||||
@@ -23,12 +23,19 @@ export function parseSSEToOpenAIResponse(rawSSE, fallbackModel) {
|
||||
const first = chunks[0];
|
||||
const contentParts = [];
|
||||
const reasoningParts = [];
|
||||
const accumulatedToolCalls = new Map<string, any>();
|
||||
type AccumulatedToolCall = {
|
||||
id: string | null;
|
||||
index: number;
|
||||
type: string;
|
||||
function: { name: string; arguments: string };
|
||||
};
|
||||
|
||||
const accumulatedToolCalls = new Map<string, AccumulatedToolCall>();
|
||||
let unknownToolCallSeq = 0;
|
||||
let finishReason = "stop";
|
||||
let usage = null;
|
||||
|
||||
const getToolCallKey = (toolCall: any) => {
|
||||
const getToolCallKey = (toolCall: Record<string, unknown>) => {
|
||||
if (toolCall?.id) return `id:${toolCall.id}`;
|
||||
if (Number.isInteger(toolCall?.index)) return `idx:${toolCall.index}`;
|
||||
unknownToolCallSeq += 1;
|
||||
|
||||
@@ -435,7 +435,7 @@ async function handleListModelsCatalog(args: { provider?: string; capability?: s
|
||||
let path = "/v1/models";
|
||||
let isProviderSpecific = false;
|
||||
let source = "local_catalog";
|
||||
let warning = undefined;
|
||||
let warning: string | undefined;
|
||||
|
||||
if (args.provider && !args.capability) {
|
||||
// Use direct provider fetch to get real-time API status
|
||||
@@ -451,7 +451,7 @@ async function handleListModelsCatalog(args: { provider?: string; capability?: s
|
||||
const raw = toRecord(await omniRouteFetch(path));
|
||||
|
||||
// If we used the direct provider endpoint
|
||||
let rawModels = [];
|
||||
let rawModels: unknown[] = [];
|
||||
if (isProviderSpecific) {
|
||||
rawModels = Array.isArray(raw.models) ? raw.models : [];
|
||||
source = typeof raw.source === "string" ? raw.source : "api";
|
||||
|
||||
@@ -522,10 +522,33 @@ export async function handleComboChat({
|
||||
},
|
||||
});
|
||||
|
||||
const transformedStream = res.body.pipeThrough(transform);
|
||||
// FIX #585: Sanitize outbound stream — strip <omniModel> tags from
|
||||
// visible content so they don't leak to the user. The tag is still
|
||||
// present in the full response for round-trip context pinning, but
|
||||
// we clean it from each SSE chunk's content field before delivery.
|
||||
const sanitize = new TransformStream({
|
||||
transform(chunk, controller) {
|
||||
const text = decoder.decode(chunk, { stream: true });
|
||||
// Only run replacement if the chunk actually contains the tag
|
||||
if (text.includes("<omniModel>")) {
|
||||
const cleaned = text.replace(
|
||||
/(?:\\\\n|\\n)?<omniModel>[^<]+<\/omniModel>(?:\\\\n|\\n)?/g,
|
||||
""
|
||||
);
|
||||
controller.enqueue(encoder.encode(cleaned));
|
||||
} else {
|
||||
controller.enqueue(chunk);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
const transformedStream = res.body.pipeThrough(transform).pipeThrough(sanitize);
|
||||
// Add model info as response header for clients that support it
|
||||
const headers = new Headers(res.headers);
|
||||
headers.set("X-OmniRoute-Model", modelStr);
|
||||
return new Response(transformedStream, {
|
||||
status: res.status,
|
||||
headers: res.headers,
|
||||
headers,
|
||||
});
|
||||
}
|
||||
: handleSingleModel;
|
||||
|
||||
@@ -5,14 +5,34 @@
|
||||
* 3 layers: trim tool messages, compress thinking, aggressive purification.
|
||||
*/
|
||||
|
||||
// Default token limits per provider (rough estimates based on model context windows)
|
||||
const DEFAULT_LIMITS = {
|
||||
import { REGISTRY } from "../config/providerRegistry.ts";
|
||||
|
||||
// Default token limits per provider (fallbacks when not in registry)
|
||||
const DEFAULT_LIMITS: Record<string, number> = {
|
||||
claude: 200000,
|
||||
openai: 128000,
|
||||
gemini: 1000000,
|
||||
codex: 400000,
|
||||
default: 128000,
|
||||
};
|
||||
|
||||
// Environment variable overrides (highest priority)
|
||||
function getEnvOverride(provider: string): number | null {
|
||||
const envKey = `CONTEXT_LENGTH_${provider.toUpperCase().replace(/[^A-Z0-9]/g, "_")}`;
|
||||
const envValue = process.env[envKey];
|
||||
if (envValue) {
|
||||
const parsed = parseInt(envValue, 10);
|
||||
if (!isNaN(parsed) && parsed > 0) return parsed;
|
||||
}
|
||||
// Global override
|
||||
const globalValue = process.env.CONTEXT_LENGTH_DEFAULT;
|
||||
if (globalValue) {
|
||||
const parsed = parseInt(globalValue, 10);
|
||||
if (!isNaN(parsed) && parsed > 0) return parsed;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// Rough chars-per-token ratio for quick estimation
|
||||
const CHARS_PER_TOKEN = 4;
|
||||
|
||||
@@ -27,9 +47,20 @@ export function estimateTokens(text) {
|
||||
|
||||
/**
|
||||
* Get token limit for a provider/model combination
|
||||
* Priority: Env override > Registry defaultContextLength > DEFAULT_LIMITS
|
||||
*/
|
||||
export function getTokenLimit(provider, model = null) {
|
||||
// Check if model has a known limit
|
||||
// 1. Check environment variable override first
|
||||
const envOverride = getEnvOverride(provider);
|
||||
if (envOverride) return envOverride;
|
||||
|
||||
// 2. Check registry for provider default
|
||||
const registryEntry = REGISTRY[provider];
|
||||
if (registryEntry?.defaultContextLength) {
|
||||
return registryEntry.defaultContextLength;
|
||||
}
|
||||
|
||||
// 3. Check if model name hints at a known limit
|
||||
if (model) {
|
||||
const lower = model.toLowerCase();
|
||||
if (lower.includes("claude")) return DEFAULT_LIMITS.claude;
|
||||
@@ -38,10 +69,13 @@ export function getTokenLimit(provider, model = null) {
|
||||
lower.includes("gpt") ||
|
||||
lower.includes("o1") ||
|
||||
lower.includes("o3") ||
|
||||
lower.includes("o4")
|
||||
lower.includes("o4") ||
|
||||
lower.includes("codex")
|
||||
)
|
||||
return DEFAULT_LIMITS.openai;
|
||||
return DEFAULT_LIMITS.codex;
|
||||
}
|
||||
|
||||
// 4. Fallback to DEFAULT_LIMITS or default
|
||||
return DEFAULT_LIMITS[provider] || DEFAULT_LIMITS.default;
|
||||
}
|
||||
|
||||
|
||||
@@ -242,8 +242,8 @@ export async function getModelInfoCore(modelStr, aliasesOrGetter) {
|
||||
// FIX #73: Models like claude-haiku-4-5-20251001 sent without provider prefix
|
||||
// would incorrectly route to OpenAI. Use heuristic prefix detection first.
|
||||
if (/^claude-/i.test(modelId)) {
|
||||
// Claude models → Antigravity (Anthropic) provider
|
||||
return { provider: "antigravity", model: modelId, extendedContext };
|
||||
// Claude models → Anthropic provider (canonical source for Claude models)
|
||||
return { provider: "anthropic", model: modelId, extendedContext };
|
||||
}
|
||||
if (/^gemini-/i.test(modelId) || /^gemma-/i.test(modelId)) {
|
||||
// Gemini/Gemma models → Gemini provider
|
||||
|
||||
@@ -41,15 +41,15 @@ function buildAnthropicCompatibleUrl(baseUrl) {
|
||||
export function detectFormatFromEndpoint(body, endpointPath = "") {
|
||||
const path = String(endpointPath || "");
|
||||
|
||||
if (/(?:^|\/)responses(?:\/.*)?$/i.test(path)) {
|
||||
if (/\/responses(?=\/|$)/i.test(path) || /^responses(?=\/|$)/i.test(path)) {
|
||||
return "openai-responses";
|
||||
}
|
||||
|
||||
if (/(?:^|\/)messages(?:\/.*)?$/i.test(path)) {
|
||||
if (/\/messages(?=\/|$)/i.test(path) || /^messages(?=\/|$)/i.test(path)) {
|
||||
return "claude";
|
||||
}
|
||||
|
||||
if (/(?:^|\/)(?:chat\/completions|completions)(?:\/.*)?$/i.test(path)) {
|
||||
if (/\/(?:chat\/completions|completions)(?=\/|$)/i.test(path) || /^(?:chat\/completions|completions)(?=\/|$)/i.test(path)) {
|
||||
return "openai";
|
||||
}
|
||||
|
||||
|
||||
@@ -86,7 +86,8 @@ function toDisplayLabel(value: string): string {
|
||||
.filter(Boolean)
|
||||
.map((part) => {
|
||||
if (/^pro\+$/i.test(part)) return "Pro+";
|
||||
if (/^[a-z]{2,}$/.test(part)) return part.charAt(0).toUpperCase() + part.slice(1).toLowerCase();
|
||||
if (/^[a-z]{2,}$/.test(part))
|
||||
return part.charAt(0).toUpperCase() + part.slice(1).toLowerCase();
|
||||
return part;
|
||||
})
|
||||
.join(" ")
|
||||
@@ -200,7 +201,9 @@ async function getGitHubUsage(accessToken, providerSpecificData) {
|
||||
if (dataRecord.quota_snapshots) {
|
||||
// Paid plan format
|
||||
const snapshots = toRecord(dataRecord.quota_snapshots);
|
||||
const resetAt = parseResetTime(getFieldValue(dataRecord, "quota_reset_date", "quotaResetDate"));
|
||||
const resetAt = parseResetTime(
|
||||
getFieldValue(dataRecord, "quota_reset_date", "quotaResetDate")
|
||||
);
|
||||
const premiumQuota = formatGitHubQuotaSnapshot(snapshots.premium_interactions, resetAt);
|
||||
const chatQuota = formatGitHubQuotaSnapshot(snapshots.chat, resetAt);
|
||||
const completionsQuota = formatGitHubQuotaSnapshot(snapshots.completions, resetAt);
|
||||
@@ -225,7 +228,11 @@ async function getGitHubUsage(accessToken, providerSpecificData) {
|
||||
// Free/limited plan format
|
||||
const monthlyQuotas = toRecord(dataRecord.monthly_quotas);
|
||||
const usedQuotas = toRecord(dataRecord.limited_user_quotas);
|
||||
const resetDate = getFieldValue(dataRecord, "limited_user_reset_date", "limitedUserResetDate");
|
||||
const resetDate = getFieldValue(
|
||||
dataRecord,
|
||||
"limited_user_reset_date",
|
||||
"limitedUserResetDate"
|
||||
);
|
||||
const resetAt = parseResetTime(resetDate);
|
||||
const quotas: Record<string, UsageQuota> = {};
|
||||
|
||||
@@ -327,11 +334,7 @@ function inferGitHubPlanName(data: JsonRecord, premiumQuota: UsageQuota | null):
|
||||
toNumber(getFieldValue(monthlyQuotas, "premium_interactions", "premiumInteractions"), 0);
|
||||
const chatTotal = toNumber(getFieldValue(monthlyQuotas, "chat", "chat"), 0);
|
||||
|
||||
if (
|
||||
combined.includes("PRO+") ||
|
||||
combined.includes("PRO_PLUS") ||
|
||||
combined.includes("PROPLUS")
|
||||
) {
|
||||
if (combined.includes("PRO+") || combined.includes("PRO_PLUS") || combined.includes("PROPLUS")) {
|
||||
return "Copilot Pro+";
|
||||
}
|
||||
if (combined.includes("ENTERPRISE")) return "Copilot Enterprise";
|
||||
@@ -655,8 +658,18 @@ async function getClaudeUsage(accessToken) {
|
||||
}
|
||||
}
|
||||
|
||||
// Try to extract plan tier from the OAuth response
|
||||
const planRaw =
|
||||
typeof data.tier === "string"
|
||||
? data.tier
|
||||
: typeof data.plan === "string"
|
||||
? data.plan
|
||||
: typeof data.subscription_type === "string"
|
||||
? data.subscription_type
|
||||
: null;
|
||||
|
||||
return {
|
||||
plan: "Claude Code",
|
||||
plan: planRaw || "Claude Code",
|
||||
quotas,
|
||||
extraUsage: data.extra_usage ?? null,
|
||||
};
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import * as fs from "fs";
|
||||
import * as path from "path";
|
||||
import { resolveDataDir } from "../../src/lib/dataPaths";
|
||||
/**
|
||||
* Responses API Transformer
|
||||
* Converts OpenAI Chat Completions SSE to Codex Responses API SSE format
|
||||
@@ -40,7 +39,8 @@ export function createResponsesLogger(model, logsDir = null) {
|
||||
|
||||
const timestamp = new Date().toISOString().replace(/[:.]/g, "").slice(0, 15);
|
||||
const uniqueId = Math.random().toString(36).slice(2, 8);
|
||||
const baseDir = logsDir || resolveDataDir();
|
||||
const baseDir = logsDir || (typeof process !== "undefined" ? process.cwd() : ".");
|
||||
// previous: const baseDir = logsDir || resolveDataDir(); — reverted in #555 for Workers compat
|
||||
const logDir = path.join(baseDir, "logs", `responses_${model}_${timestamp}_${uniqueId}`);
|
||||
|
||||
try {
|
||||
|
||||
@@ -29,7 +29,7 @@ type ClaudeTool = {
|
||||
|
||||
/**
|
||||
* T02: Recursively strips empty text blocks from content arrays.
|
||||
* Anthropic returns 400 "text content blocks must be non-empty" if any
|
||||
* Anthropic returns 400 "text content blocks must be non-empty" when a
|
||||
* text block has text: "". Must also recurse into nested tool_result.content.
|
||||
* Ref: sub2api PR #1212
|
||||
*/
|
||||
@@ -259,11 +259,19 @@ export function openaiToClaudeRequest(model, body, stream) {
|
||||
toolNameMap.set(toolName, originalName);
|
||||
}
|
||||
|
||||
// Normalize input_schema: Anthropic requires `properties` when type is "object" (#595).
|
||||
// MCP tools (e.g. pencil, computer_use) may omit properties on object-type schemas.
|
||||
const rawSchema: Record<string, unknown> =
|
||||
toolData.parameters || toolData.input_schema || { type: "object", properties: {}, required: [] };
|
||||
const normalizedSchema =
|
||||
rawSchema.type === "object" && !rawSchema.properties
|
||||
? { ...rawSchema, properties: {} }
|
||||
: rawSchema;
|
||||
|
||||
return {
|
||||
name: toolName,
|
||||
description: toolData.description || "",
|
||||
input_schema: toolData.parameters ||
|
||||
toolData.input_schema || { type: "object", properties: {}, required: [] },
|
||||
input_schema: normalizedSchema,
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
@@ -57,6 +57,8 @@ type TranslateState = ReturnType<typeof initState> & {
|
||||
accumulatedContent?: string;
|
||||
};
|
||||
|
||||
type UsageTokenRecord = Record<string, number>;
|
||||
|
||||
function getOpenAIIntermediateChunks(value: unknown): unknown[] {
|
||||
if (!value || typeof value !== "object") return [];
|
||||
const candidate = (value as JsonRecord)._openaiIntermediate;
|
||||
@@ -108,7 +110,9 @@ export function createSSEStream(options: StreamOptions = {}) {
|
||||
} = options;
|
||||
|
||||
let buffer = "";
|
||||
let usage = null;
|
||||
let usage: UsageTokenRecord | null = null;
|
||||
/** Passthrough (OpenAI CC shape): saw tool_calls in stream before finish_reason */
|
||||
let passthroughHasToolCalls = false;
|
||||
|
||||
// State for translate mode (accumulatedContent for call log response body)
|
||||
const state: TranslateState | null =
|
||||
@@ -223,9 +227,9 @@ export function createSSEStream(options: StreamOptions = {}) {
|
||||
if (extracted) {
|
||||
// Non-destructive merge: never overwrite a positive value with 0
|
||||
// message_start carries input_tokens, message_delta carries output_tokens;
|
||||
if (!usage) usage = {} as any;
|
||||
const u = usage as Record<string, number>;
|
||||
const eu = extracted as Record<string, number>;
|
||||
if (!usage) usage = {};
|
||||
const u = usage;
|
||||
const eu = extracted as UsageTokenRecord;
|
||||
if (eu.prompt_tokens > 0) u.prompt_tokens = eu.prompt_tokens;
|
||||
if (eu.completion_tokens > 0) u.completion_tokens = eu.completion_tokens;
|
||||
if (eu.total_tokens > 0) u.total_tokens = eu.total_tokens;
|
||||
@@ -266,7 +270,7 @@ export function createSSEStream(options: StreamOptions = {}) {
|
||||
|
||||
// T18: Track if we saw tool calls
|
||||
if (delta?.tool_calls && delta.tool_calls.length > 0) {
|
||||
(state as any).passthroughHasToolCalls = true;
|
||||
passthroughHasToolCalls = true;
|
||||
}
|
||||
|
||||
const content = delta?.content || delta?.reasoning_content;
|
||||
@@ -288,7 +292,7 @@ export function createSSEStream(options: StreamOptions = {}) {
|
||||
// T18: Normalize finish_reason to 'tool_calls' if tool calls were used
|
||||
if (
|
||||
isFinishChunk &&
|
||||
(state as any).passthroughHasToolCalls &&
|
||||
passthroughHasToolCalls &&
|
||||
parsed.choices[0].finish_reason !== "tool_calls"
|
||||
) {
|
||||
parsed.choices[0].finish_reason = "tool_calls";
|
||||
|
||||
@@ -140,7 +140,7 @@ export function createDisconnectAwareStream(transformStream, streamController) {
|
||||
const errorMsg = error instanceof Error ? error.message : "Upstream stream error";
|
||||
const statusCode =
|
||||
typeof error === "object" && error !== null && "statusCode" in error
|
||||
? (error as any).statusCode
|
||||
? Number((error as { statusCode?: unknown }).statusCode) || 500
|
||||
: 500;
|
||||
|
||||
const errorEvent = {
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "omniroute",
|
||||
"version": "3.0.0-rc.15",
|
||||
"version": "3.0.1",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "omniroute",
|
||||
"version": "3.0.0-rc.15",
|
||||
"version": "3.0.1",
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"workspaces": [
|
||||
@@ -14606,15 +14606,6 @@
|
||||
"marked": "14.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/monaco-editor/node_modules/dompurify": {
|
||||
"version": "3.2.7",
|
||||
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.2.7.tgz",
|
||||
"integrity": "sha512-WhL/YuveyGXJaerVlMYGWhvQswa7myDG17P7Vu65EWC05o8vfeNbvNf4d/BOvH99+ZW+LlQsc1GDKMa1vNK6dw==",
|
||||
"license": "(MPL-2.0 OR Apache-2.0)",
|
||||
"optionalDependencies": {
|
||||
"@types/trusted-types": "^2.0.7"
|
||||
}
|
||||
},
|
||||
"node_modules/motion": {
|
||||
"version": "12.38.0",
|
||||
"resolved": "https://registry.npmjs.org/motion/-/motion-12.38.0.tgz",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "omniroute",
|
||||
"version": "3.0.0-rc.15",
|
||||
"version": "3.0.1",
|
||||
"description": "Smart AI Router with auto fallback — route to FREE & cheap models, zero downtime. Works with Cursor, Cline, Claude Desktop, Codex, and any OpenAI-compatible tool.",
|
||||
"type": "module",
|
||||
"bin": {
|
||||
@@ -47,7 +47,7 @@
|
||||
"homepage": "https://omniroute.online",
|
||||
"scripts": {
|
||||
"dev": "node scripts/run-next.mjs dev",
|
||||
"build": "next build",
|
||||
"build": "node scripts/build-next-isolated.mjs",
|
||||
"build:cli": "node scripts/prepublish.mjs",
|
||||
"start": "node scripts/run-next.mjs start",
|
||||
"lint": "eslint .",
|
||||
@@ -155,5 +155,8 @@
|
||||
"omniroute",
|
||||
"sharp"
|
||||
]
|
||||
},
|
||||
"overrides": {
|
||||
"dompurify": "^3.3.2"
|
||||
}
|
||||
}
|
||||
|
||||
|
After Width: | Height: | Size: 4.7 KiB |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128 128" fill="none"><rect width="128" height="128" rx="24" fill="#1e293b"/><path d="M48 60a16 16 0 1 1 12 15.5V88h-8V75.5A16 16 0 0 1 48 60z" fill="none" stroke="#94a3b8" stroke-width="3"/><circle cx="56" cy="56" r="5" fill="#94a3b8"/><path d="M64 76h24M76 72v8M84 72v8" stroke="#94a3b8" stroke-width="3"/></svg>
|
||||
|
After Width: | Height: | Size: 367 B |
|
After Width: | Height: | Size: 4.7 KiB |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128 128" fill="none"><rect width="128" height="128" rx="24" fill="#0f172a"/><path d="M40 40h48v48H40z" fill="none" stroke="#3b82f6" stroke-width="3"/><path d="M52 52h24v24H52z" fill="#3b82f6" opacity="0.4"/><path d="M58 58h12v12H58z" fill="#60a5fa"/></svg>
|
||||
|
After Width: | Height: | Size: 310 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128 128" fill="none"><rect width="128" height="128" rx="24" fill="#1a1a2e"/><path d="M32 48h20v8H32zm44 0h20v8H76zm-22 24h20v8H54z" fill="#7c3aed"/><circle cx="52" cy="52" r="4" fill="#a78bfa"/><circle cx="76" cy="76" r="4" fill="#a78bfa"/><path d="M52 52l22 24" stroke="#7c3aed" stroke-width="2"/></svg>
|
||||
|
After Width: | Height: | Size: 358 B |
@@ -0,0 +1,52 @@
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<title>404</title>
|
||||
<style>
|
||||
@font-face {
|
||||
font-family: 'colfax-web';
|
||||
font-display: swap;
|
||||
src: url('https://www.datocms.com/fonts/colfax-web-regular.woff2') format('woff2'), url('https://www.datocms.com/fonts/colfax-web-regular.woff') format('woff');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'colfax-web';
|
||||
font-display: swap;
|
||||
font-weight: 700;
|
||||
src: url('https://www.datocms.com/fonts/colfax-web-bold.woff2') format('woff2'), url('https://www.datocms.com/fonts/colfax-web-bold.woff') format('woff');
|
||||
}
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
body {
|
||||
box-sizing: border-box;
|
||||
font-family: colfax;
|
||||
display: grid;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
text-align: center;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
main {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 30px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 50px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<main>
|
||||
<h1>404</h1>
|
||||
<p>Not Found</p>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="95" height="88" fill="none">
|
||||
<path fill="#FFD21E" d="M47.21 76.5a34.75 34.75 0 1 0 0-69.5 34.75 34.75 0 0 0 0 69.5Z" />
|
||||
<path
|
||||
fill="#FF9D0B"
|
||||
d="M81.96 41.75a34.75 34.75 0 1 0-69.5 0 34.75 34.75 0 0 0 69.5 0Zm-73.5 0a38.75 38.75 0 1 1 77.5 0 38.75 38.75 0 0 1-77.5 0Z"
|
||||
/>
|
||||
<path
|
||||
fill="#3A3B45"
|
||||
d="M58.5 32.3c1.28.44 1.78 3.06 3.07 2.38a5 5 0 1 0-6.76-2.07c.61 1.15 2.55-.72 3.7-.32ZM34.95 32.3c-1.28.44-1.79 3.06-3.07 2.38a5 5 0 1 1 6.76-2.07c-.61 1.15-2.56-.72-3.7-.32Z"
|
||||
/>
|
||||
<path
|
||||
fill="#FF323D"
|
||||
d="M46.96 56.29c9.83 0 13-8.76 13-13.26 0-2.34-1.57-1.6-4.09-.36-2.33 1.15-5.46 2.74-8.9 2.74-7.19 0-13-6.88-13-2.38s3.16 13.26 13 13.26Z"
|
||||
/>
|
||||
<path
|
||||
fill="#3A3B45"
|
||||
fill-rule="evenodd"
|
||||
d="M39.43 54a8.7 8.7 0 0 1 5.3-4.49c.4-.12.81.57 1.24 1.28.4.68.82 1.37 1.24 1.37.45 0 .9-.68 1.33-1.35.45-.7.89-1.38 1.32-1.25a8.61 8.61 0 0 1 5 4.17c3.73-2.94 5.1-7.74 5.1-10.7 0-2.34-1.57-1.6-4.09-.36l-.14.07c-2.31 1.15-5.39 2.67-8.77 2.67s-6.45-1.52-8.77-2.67c-2.6-1.29-4.23-2.1-4.23.29 0 3.05 1.46 8.06 5.47 10.97Z"
|
||||
clip-rule="evenodd"
|
||||
/>
|
||||
<path
|
||||
fill="#FF9D0B"
|
||||
d="M70.71 37a3.25 3.25 0 1 0 0-6.5 3.25 3.25 0 0 0 0 6.5ZM24.21 37a3.25 3.25 0 1 0 0-6.5 3.25 3.25 0 0 0 0 6.5ZM17.52 48c-1.62 0-3.06.66-4.07 1.87a5.97 5.97 0 0 0-1.33 3.76 7.1 7.1 0 0 0-1.94-.3c-1.55 0-2.95.59-3.94 1.66a5.8 5.8 0 0 0-.8 7 5.3 5.3 0 0 0-1.79 2.82c-.24.9-.48 2.8.8 4.74a5.22 5.22 0 0 0-.37 5.02c1.02 2.32 3.57 4.14 8.52 6.1 3.07 1.22 5.89 2 5.91 2.01a44.33 44.33 0 0 0 10.93 1.6c5.86 0 10.05-1.8 12.46-5.34 3.88-5.69 3.33-10.9-1.7-15.92-2.77-2.78-4.62-6.87-5-7.77-.78-2.66-2.84-5.62-6.25-5.62a5.7 5.7 0 0 0-4.6 2.46c-1-1.26-1.98-2.25-2.86-2.82A7.4 7.4 0 0 0 17.52 48Zm0 4c.51 0 1.14.22 1.82.65 2.14 1.36 6.25 8.43 7.76 11.18.5.92 1.37 1.31 2.14 1.31 1.55 0 2.75-1.53.15-3.48-3.92-2.93-2.55-7.72-.68-8.01.08-.02.17-.02.24-.02 1.7 0 2.45 2.93 2.45 2.93s2.2 5.52 5.98 9.3c3.77 3.77 3.97 6.8 1.22 10.83-1.88 2.75-5.47 3.58-9.16 3.58-3.81 0-7.73-.9-9.92-1.46-.11-.03-13.45-3.8-11.76-7 .28-.54.75-.76 1.34-.76 2.38 0 6.7 3.54 8.57 3.54.41 0 .7-.17.83-.6.79-2.85-12.06-4.05-10.98-8.17.2-.73.71-1.02 1.44-1.02 3.14 0 10.2 5.53 11.68 5.53.11 0 .2-.03.24-.1.74-1.2.33-2.04-4.9-5.2-5.21-3.16-8.88-5.06-6.8-7.33.24-.26.58-.38 1-.38 3.17 0 10.66 6.82 10.66 6.82s2.02 2.1 3.25 2.1c.28 0 .52-.1.68-.38.86-1.46-8.06-8.22-8.56-11.01-.34-1.9.24-2.85 1.31-2.85Z"
|
||||
/>
|
||||
<path
|
||||
fill="#FFD21E"
|
||||
d="M38.6 76.69c2.75-4.04 2.55-7.07-1.22-10.84-3.78-3.77-5.98-9.3-5.98-9.3s-.82-3.2-2.69-2.9c-1.87.3-3.24 5.08.68 8.01 3.91 2.93-.78 4.92-2.29 2.17-1.5-2.75-5.62-9.82-7.76-11.18-2.13-1.35-3.63-.6-3.13 2.2.5 2.79 9.43 9.55 8.56 11-.87 1.47-3.93-1.71-3.93-1.71s-9.57-8.71-11.66-6.44c-2.08 2.27 1.59 4.17 6.8 7.33 5.23 3.16 5.64 4 4.9 5.2-.75 1.2-12.28-8.53-13.36-4.4-1.08 4.11 11.77 5.3 10.98 8.15-.8 2.85-9.06-5.38-10.74-2.18-1.7 3.21 11.65 6.98 11.76 7.01 4.3 1.12 15.25 3.49 19.08-2.12Z"
|
||||
/>
|
||||
<path
|
||||
fill="#FF9D0B"
|
||||
d="M77.4 48c1.62 0 3.07.66 4.07 1.87a5.97 5.97 0 0 1 1.33 3.76 7.1 7.1 0 0 1 1.95-.3c1.55 0 2.95.59 3.94 1.66a5.8 5.8 0 0 1 .8 7 5.3 5.3 0 0 1 1.78 2.82c.24.9.48 2.8-.8 4.74a5.22 5.22 0 0 1 .37 5.02c-1.02 2.32-3.57 4.14-8.51 6.1-3.08 1.22-5.9 2-5.92 2.01a44.33 44.33 0 0 1-10.93 1.6c-5.86 0-10.05-1.8-12.46-5.34-3.88-5.69-3.33-10.9 1.7-15.92 2.78-2.78 4.63-6.87 5.01-7.77.78-2.66 2.83-5.62 6.24-5.62a5.7 5.7 0 0 1 4.6 2.46c1-1.26 1.98-2.25 2.87-2.82A7.4 7.4 0 0 1 77.4 48Zm0 4c-.51 0-1.13.22-1.82.65-2.13 1.36-6.25 8.43-7.76 11.18a2.43 2.43 0 0 1-2.14 1.31c-1.54 0-2.75-1.53-.14-3.48 3.91-2.93 2.54-7.72.67-8.01a1.54 1.54 0 0 0-.24-.02c-1.7 0-2.45 2.93-2.45 2.93s-2.2 5.52-5.97 9.3c-3.78 3.77-3.98 6.8-1.22 10.83 1.87 2.75 5.47 3.58 9.15 3.58 3.82 0 7.73-.9 9.93-1.46.1-.03 13.45-3.8 11.76-7-.29-.54-.75-.76-1.34-.76-2.38 0-6.71 3.54-8.57 3.54-.42 0-.71-.17-.83-.6-.8-2.85 12.05-4.05 10.97-8.17-.19-.73-.7-1.02-1.44-1.02-3.14 0-10.2 5.53-11.68 5.53-.1 0-.19-.03-.23-.1-.74-1.2-.34-2.04 4.88-5.2 5.23-3.16 8.9-5.06 6.8-7.33-.23-.26-.57-.38-.98-.38-3.18 0-10.67 6.82-10.67 6.82s-2.02 2.1-3.24 2.1a.74.74 0 0 1-.68-.38c-.87-1.46 8.05-8.22 8.55-11.01.34-1.9-.24-2.85-1.31-2.85Z"
|
||||
/>
|
||||
<path
|
||||
fill="#FFD21E"
|
||||
d="M56.33 76.69c-2.75-4.04-2.56-7.07 1.22-10.84 3.77-3.77 5.97-9.3 5.97-9.3s.82-3.2 2.7-2.9c1.86.3 3.23 5.08-.68 8.01-3.92 2.93.78 4.92 2.28 2.17 1.51-2.75 5.63-9.82 7.76-11.18 2.13-1.35 3.64-.6 3.13 2.2-.5 2.79-9.42 9.55-8.55 11 .86 1.47 3.92-1.71 3.92-1.71s9.58-8.71 11.66-6.44c2.08 2.27-1.58 4.17-6.8 7.33-5.23 3.16-5.63 4-4.9 5.2.75 1.2 12.28-8.53 13.36-4.4 1.08 4.11-11.76 5.3-10.97 8.15.8 2.85 9.05-5.38 10.74-2.18 1.69 3.21-11.65 6.98-11.76 7.01-4.31 1.12-15.26 3.49-19.08-2.12Z"
|
||||
/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.5 KiB |
|
After Width: | Height: | Size: 18 KiB |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128 128" fill="none"><rect width="128" height="128" rx="24" fill="#1e293b"/><circle cx="64" cy="64" r="28" fill="none" stroke="#60a5fa" stroke-width="3"/><path d="M64 36v56M36 64h56" stroke="#60a5fa" stroke-width="2" opacity="0.3"/><circle cx="64" cy="64" r="8" fill="#3b82f6"/></svg>
|
||||
|
After Width: | Height: | Size: 338 B |
@@ -0,0 +1,18 @@
|
||||
<svg width="234" height="42" viewBox="0 0 234 42" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M18 30H6V18H18V30Z" fill="#4B4646"/>
|
||||
<path d="M18 12H6V30H18V12ZM24 36H0V6H24V36Z" fill="#B7B1B1"/>
|
||||
<path d="M48 30H36V18H48V30Z" fill="#4B4646"/>
|
||||
<path d="M36 30H48V12H36V30ZM54 36H36V42H30V6H54V36Z" fill="#B7B1B1"/>
|
||||
<path d="M84 24V30H66V24H84Z" fill="#4B4646"/>
|
||||
<path d="M84 24H66V30H84V36H60V6H84V24ZM66 18H78V12H66V18Z" fill="#B7B1B1"/>
|
||||
<path d="M108 36H96V18H108V36Z" fill="#4B4646"/>
|
||||
<path d="M108 12H96V36H90V6H108V12ZM114 36H108V12H114V36Z" fill="#B7B1B1"/>
|
||||
<path d="M144 30H126V18H144V30Z" fill="#4B4646"/>
|
||||
<path d="M144 12H126V30H144V36H120V6H144V12Z" fill="#F1ECEC"/>
|
||||
<path d="M168 30H156V18H168V30Z" fill="#4B4646"/>
|
||||
<path d="M168 12H156V30H168V12ZM174 36H150V6H174V36Z" fill="#F1ECEC"/>
|
||||
<path d="M198 30H186V18H198V30Z" fill="#4B4646"/>
|
||||
<path d="M198 12H186V30H198V12ZM204 36H180V6H198V0H204V36Z" fill="#F1ECEC"/>
|
||||
<path d="M234 24V30H216V24H234Z" fill="#4B4646"/>
|
||||
<path d="M216 12V18H228V12H216ZM234 24H216V30H234V36H210V6H234V24Z" fill="#F1ECEC"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
@@ -0,0 +1,18 @@
|
||||
<svg width="234" height="42" viewBox="0 0 234 42" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M18 30H6V18H18V30Z" fill="#4B4646"/>
|
||||
<path d="M18 12H6V30H18V12ZM24 36H0V6H24V36Z" fill="#B7B1B1"/>
|
||||
<path d="M48 30H36V18H48V30Z" fill="#4B4646"/>
|
||||
<path d="M36 30H48V12H36V30ZM54 36H36V42H30V6H54V36Z" fill="#B7B1B1"/>
|
||||
<path d="M84 24V30H66V24H84Z" fill="#4B4646"/>
|
||||
<path d="M84 24H66V30H84V36H60V6H84V24ZM66 18H78V12H66V18Z" fill="#B7B1B1"/>
|
||||
<path d="M108 36H96V18H108V36Z" fill="#4B4646"/>
|
||||
<path d="M108 12H96V36H90V6H108V12ZM114 36H108V12H114V36Z" fill="#B7B1B1"/>
|
||||
<path d="M144 30H126V18H144V30Z" fill="#4B4646"/>
|
||||
<path d="M144 12H126V30H144V36H120V6H144V12Z" fill="#F1ECEC"/>
|
||||
<path d="M168 30H156V18H168V30Z" fill="#4B4646"/>
|
||||
<path d="M168 12H156V30H168V12ZM174 36H150V6H174V36Z" fill="#F1ECEC"/>
|
||||
<path d="M198 30H186V18H198V30Z" fill="#4B4646"/>
|
||||
<path d="M198 12H186V30H198V12ZM204 36H180V6H198V0H204V36Z" fill="#F1ECEC"/>
|
||||
<path d="M234 24V30H216V24H234Z" fill="#4B4646"/>
|
||||
<path d="M216 12V18H228V12H216ZM234 24H216V30H234V36H210V6H234V24Z" fill="#F1ECEC"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128 128" fill="none"><rect width="128" height="128" rx="24" fill="#0c0a09"/><text x="64" y="74" font-family="sans-serif" font-size="36" font-weight="bold" fill="#fbbf24" text-anchor="middle">P</text><circle cx="64" cy="60" r="30" fill="none" stroke="#fbbf24" stroke-width="2" opacity="0.3"/></svg>
|
||||
|
After Width: | Height: | Size: 351 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128 128" fill="none"><rect width="128" height="128" rx="24" fill="#1a1a2e"/><text x="64" y="70" font-family="sans-serif" font-size="40" font-weight="bold" fill="#f97316" text-anchor="middle">SD</text><text x="64" y="96" font-family="sans-serif" font-size="16" fill="#94a3b8" text-anchor="middle">WebUI</text></svg>
|
||||
|
After Width: | Height: | Size: 368 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128 128" fill="none"><rect width="128" height="128" rx="24" fill="#1a1a2e"/><circle cx="64" cy="50" r="16" fill="none" stroke="#10b981" stroke-width="3"/><circle cx="44" cy="86" r="12" fill="none" stroke="#10b981" stroke-width="3"/><circle cx="84" cy="86" r="12" fill="none" stroke="#10b981" stroke-width="3"/><path d="M56 62l-8 16M72 62l8 16M52 86h24" stroke="#10b981" stroke-width="2"/></svg>
|
||||
|
After Width: | Height: | Size: 448 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128 128" fill="none"><rect width="128" height="128" rx="24" fill="#1a1a2e"/><path d="M64 24L104 96H24L64 24z" fill="#4285F4" opacity="0.8"/><path d="M64 44L84 80H44L64 44z" fill="#34A853" opacity="0.9"/><circle cx="64" cy="68" r="8" fill="#FBBC04"/></svg>
|
||||
|
After Width: | Height: | Size: 309 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128 128" fill="none"><rect width="128" height="128" rx="24" fill="#1e1b4b"/><text x="64" y="76" font-family="sans-serif" font-size="48" font-weight="bold" fill="#818cf8" text-anchor="middle">Z</text><text x="64" y="100" font-family="sans-serif" font-size="18" fill="#6366f1" text-anchor="middle">.AI</text></svg>
|
||||
|
After Width: | Height: | Size: 366 B |
@@ -0,0 +1,84 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import fs from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
import { spawn } from "node:child_process";
|
||||
|
||||
/**
|
||||
* This repository contains a legacy `app/` snapshot (packaging/runtime artifacts)
|
||||
* alongside the active Next.js source in `src/app/`. Next.js route discovery scans
|
||||
* both and fails the build on legacy files. We temporarily move the legacy folder
|
||||
* out of the project root during `next build`, then restore it in all outcomes.
|
||||
*/
|
||||
|
||||
const projectRoot = process.cwd();
|
||||
const legacyAppDir = path.join(projectRoot, "app");
|
||||
const backupDir = path.join(projectRoot, `.app-build-backup-${process.pid}-${Date.now()}`);
|
||||
|
||||
async function exists(targetPath) {
|
||||
try {
|
||||
await fs.access(targetPath);
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function runNextBuild() {
|
||||
return new Promise((resolve) => {
|
||||
const nextBin = path.join(projectRoot, "node_modules", "next", "dist", "bin", "next");
|
||||
const child = spawn(process.execPath, [nextBin, "build"], {
|
||||
cwd: projectRoot,
|
||||
stdio: "inherit",
|
||||
env: process.env,
|
||||
});
|
||||
|
||||
const forward = (signal) => {
|
||||
if (!child.killed) child.kill(signal);
|
||||
};
|
||||
|
||||
process.on("SIGINT", forward);
|
||||
process.on("SIGTERM", forward);
|
||||
|
||||
child.on("exit", (code, signal) => {
|
||||
process.off("SIGINT", forward);
|
||||
process.off("SIGTERM", forward);
|
||||
if (signal) {
|
||||
resolve({ code: 1, signal });
|
||||
return;
|
||||
}
|
||||
resolve({ code: code ?? 1, signal: null });
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async function main() {
|
||||
let moved = false;
|
||||
|
||||
try {
|
||||
if (await exists(legacyAppDir)) {
|
||||
await fs.rename(legacyAppDir, backupDir);
|
||||
moved = true;
|
||||
}
|
||||
|
||||
const result = await runNextBuild();
|
||||
process.exitCode = result.code;
|
||||
} catch (error) {
|
||||
console.error("[build-next-isolated] Build failed:", error);
|
||||
process.exitCode = 1;
|
||||
} finally {
|
||||
if (moved) {
|
||||
try {
|
||||
await fs.rename(backupDir, legacyAppDir);
|
||||
} catch (restoreError) {
|
||||
console.error(
|
||||
`[build-next-isolated] Failed to restore legacy app dir from ${backupDir}:`,
|
||||
restoreError
|
||||
);
|
||||
process.exitCode = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await main();
|
||||
@@ -240,7 +240,7 @@ if (existsSync(mitmSrc)) {
|
||||
writeFileSync(tmpTsconfigPath, JSON.stringify(mitmTsconfig, null, 2));
|
||||
|
||||
try {
|
||||
execSync(`npx tsc -p ${tmpTsconfigPath}`, { cwd: ROOT, stdio: "inherit" });
|
||||
execSync("npx tsc -p " + JSON.stringify(tmpTsconfigPath), { cwd: ROOT, stdio: "inherit" });
|
||||
console.log(" ✅ MITM utilities compiled to app/src/mitm/");
|
||||
} catch (err) {
|
||||
console.warn(" ⚠️ MITM compile warning (non-fatal):", err.message);
|
||||
|
||||
@@ -9,15 +9,15 @@ import { bootstrapEnv } from "./bootstrap-env.mjs";
|
||||
|
||||
const mode = process.argv[2] === "start" ? "start" : "dev";
|
||||
|
||||
const runtimePorts = resolveRuntimePorts();
|
||||
// Load .env / server.env first so PORT / DASHBOARD_PORT from files affect --port below.
|
||||
const env = bootstrapEnv();
|
||||
const runtimePorts = resolveRuntimePorts(env);
|
||||
const { dashboardPort } = runtimePorts;
|
||||
|
||||
// Auto-generate secrets on first run, merge .env + process.env
|
||||
const env = bootstrapEnv();
|
||||
|
||||
const args = ["./node_modules/next/dist/bin/next", mode, "--port", String(dashboardPort)];
|
||||
// Default: use webpack (stable). Set OMNIROUTE_USE_TURBOPACK=1 to use Turbopack (faster dev).
|
||||
if (mode === "dev" && process.env.OMNIROUTE_USE_TURBOPACK !== "1") {
|
||||
// Default: use webpack (stable). Set OMNIROUTE_USE_TURBOPACK=1 in .env for Turbopack (faster dev).
|
||||
// Must read merged `env` from bootstrap — .env is not applied to process.env in the launcher.
|
||||
if (mode === "dev" && env.OMNIROUTE_USE_TURBOPACK !== "1") {
|
||||
args.splice(2, 0, "--webpack");
|
||||
}
|
||||
|
||||
|
||||
@@ -7,10 +7,8 @@ import {
|
||||
} from "./runtime-env.mjs";
|
||||
import { bootstrapEnv } from "./bootstrap-env.mjs";
|
||||
|
||||
const runtimePorts = resolveRuntimePorts();
|
||||
|
||||
// Auto-generate secrets on first run, merge .env + process.env
|
||||
const env = bootstrapEnv();
|
||||
const runtimePorts = resolveRuntimePorts(env);
|
||||
|
||||
spawnWithForwardedSignals("node", ["server.js"], {
|
||||
stdio: "inherit",
|
||||
|
||||
@@ -5,10 +5,14 @@ export function parsePort(value, fallback) {
|
||||
return Number.isFinite(parsed) && parsed > 0 && parsed <= 65535 ? parsed : fallback;
|
||||
}
|
||||
|
||||
export function resolveRuntimePorts() {
|
||||
const basePort = parsePort(process.env.PORT || "20128", 20128);
|
||||
const apiPort = parsePort(process.env.API_PORT || String(basePort), basePort);
|
||||
const dashboardPort = parsePort(process.env.DASHBOARD_PORT || String(basePort), basePort);
|
||||
/**
|
||||
* @param {NodeJS.ProcessEnv | Record<string, string | undefined>} [fromEnv]
|
||||
* Defaults to process.env. Pass bootstrap `merged` so project `.env` PORT applies before spawn.
|
||||
*/
|
||||
export function resolveRuntimePorts(fromEnv = process.env) {
|
||||
const basePort = parsePort(fromEnv.PORT || "20128", 20128);
|
||||
const apiPort = parsePort(fromEnv.API_PORT || String(basePort), basePort);
|
||||
const dashboardPort = parsePort(fromEnv.DASHBOARD_PORT || String(basePort), basePort);
|
||||
|
||||
return { basePort, apiPort, dashboardPort };
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { Card, CardSkeleton, Button, Modal } from "@/shared/components";
|
||||
import ProviderIcon from "@/shared/components/ProviderIcon";
|
||||
import { AI_PROVIDERS, FREE_PROVIDERS, OAUTH_PROVIDERS } from "@/shared/constants/providers";
|
||||
import { useNotificationStore } from "@/store/notificationStore";
|
||||
import { copyToClipboard } from "@/shared/utils/clipboard";
|
||||
@@ -356,7 +357,6 @@ HomePageClient.propTypes = {
|
||||
};
|
||||
|
||||
function ProviderOverviewCard({ item, metrics, onClick }) {
|
||||
const [imgError, setImgError] = useState(false);
|
||||
const t = useTranslations("home");
|
||||
const tc = useTranslations("common");
|
||||
|
||||
@@ -380,24 +380,7 @@ function ProviderOverviewCard({ item, metrics, onClick }) {
|
||||
className="size-8 rounded-lg flex items-center justify-center shrink-0"
|
||||
style={{ backgroundColor: `${item.provider.color || "#888"}15` }}
|
||||
>
|
||||
{imgError ? (
|
||||
<span
|
||||
className="text-[10px] font-bold"
|
||||
style={{ color: item.provider.color || "#888" }}
|
||||
>
|
||||
{item.provider.textIcon || item.provider.id.slice(0, 2).toUpperCase()}
|
||||
</span>
|
||||
) : (
|
||||
<Image
|
||||
src={`/providers/${item.provider.id}.png`}
|
||||
alt={item.provider.name}
|
||||
width={26}
|
||||
height={26}
|
||||
className="object-contain rounded-lg"
|
||||
sizes="26px"
|
||||
onError={() => setImgError(true)}
|
||||
/>
|
||||
)}
|
||||
<ProviderIcon providerId={item.provider.id} size={26} type="color" />
|
||||
</div>
|
||||
|
||||
<div className="min-w-0 flex-1">
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
"use client";
|
||||
|
||||
import { useState, useEffect, useCallback } from "react";
|
||||
import Link from "next/link";
|
||||
import { Card, Button, Input } from "@/shared/components";
|
||||
import ProviderIcon from "@/shared/components/ProviderIcon";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { AI_PROVIDERS } from "@/shared/constants/config";
|
||||
import { CLI_COMPAT_PROVIDER_IDS } from "@/shared/constants/cliCompatProviders";
|
||||
|
||||
interface AgentInfo {
|
||||
id: string;
|
||||
@@ -23,6 +26,35 @@ interface AgentSummary {
|
||||
custom: number;
|
||||
}
|
||||
|
||||
// Map agent binary IDs to provider icon IDs for ProviderIcon component
|
||||
const AGENT_ICON_MAP: Record<string, string> = {
|
||||
claude: "anthropic",
|
||||
"claude-code": "anthropic",
|
||||
codex: "openai",
|
||||
"gemini-cli": "google",
|
||||
gemini: "google",
|
||||
opencode: "opencode",
|
||||
openclaw: "openclaw",
|
||||
cline: "cline",
|
||||
kilocode: "kilocode",
|
||||
kilo: "kilocode",
|
||||
cursor: "cursor",
|
||||
antigravity: "antigravity",
|
||||
droid: "droid",
|
||||
goose: "goose",
|
||||
aider: "aider",
|
||||
iflow: "iflow",
|
||||
kiro: "kiro",
|
||||
nanobot: "nanobot",
|
||||
picoclaw: "picoclaw",
|
||||
zeroclaw: "zeroclaw",
|
||||
ironclaw: "ironclaw",
|
||||
};
|
||||
|
||||
function getAgentIconId(agentId: string): string | null {
|
||||
return AGENT_ICON_MAP[agentId] || null;
|
||||
}
|
||||
|
||||
export default function AgentsPage() {
|
||||
const [agents, setAgents] = useState<AgentInfo[]>([]);
|
||||
const [summary, setSummary] = useState<AgentSummary | null>(null);
|
||||
@@ -137,8 +169,9 @@ export default function AgentsPage() {
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="flex items-center justify-center min-h-[400px]">
|
||||
<div className="flex flex-col items-center justify-center min-h-[400px] gap-3">
|
||||
<div className="animate-spin rounded-full h-8 w-8 border-2 border-primary border-t-transparent" />
|
||||
<p className="text-sm text-text-muted">{t("scanning")}</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -179,6 +212,51 @@ export default function AgentsPage() {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Setup Guide */}
|
||||
<Card>
|
||||
<div className="flex items-center justify-between gap-3 mb-4">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="p-2 rounded-lg bg-blue-500/10 text-blue-500">
|
||||
<span className="material-symbols-outlined text-[20px]" aria-hidden="true">
|
||||
support
|
||||
</span>
|
||||
</div>
|
||||
<h3 className="text-lg font-semibold">{t("setupGuideTitle")}</h3>
|
||||
</div>
|
||||
<Link
|
||||
href="/dashboard/cli-tools"
|
||||
className="text-xs px-2.5 py-1.5 rounded-lg border border-border/60 hover:bg-surface/40 transition-colors"
|
||||
>
|
||||
{t("openCliTools")}
|
||||
</Link>
|
||||
</div>
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-3">
|
||||
<div className="rounded-lg border border-border/50 bg-black/[0.02] dark:bg-white/[0.02] p-3">
|
||||
<div className="flex items-center gap-2 mb-1.5">
|
||||
<span className="material-symbols-outlined text-[16px] text-blue-500">radar</span>
|
||||
<p className="text-sm font-medium">{t("setupGuideDetectCliTitle")}</p>
|
||||
</div>
|
||||
<p className="text-xs text-text-muted">{t("setupGuideDetectCliDesc")}</p>
|
||||
</div>
|
||||
<div className="rounded-lg border border-border/50 bg-black/[0.02] dark:bg-white/[0.02] p-3">
|
||||
<div className="flex items-center gap-2 mb-1.5">
|
||||
<span className="material-symbols-outlined text-[16px] text-amber-500">build</span>
|
||||
<p className="text-sm font-medium">{t("setupGuideCustomAgentTitle")}</p>
|
||||
</div>
|
||||
<p className="text-xs text-text-muted">{t("setupGuideCustomAgentDesc")}</p>
|
||||
</div>
|
||||
<div className="rounded-lg border border-border/50 bg-black/[0.02] dark:bg-white/[0.02] p-3">
|
||||
<div className="flex items-center gap-2 mb-1.5">
|
||||
<span className="material-symbols-outlined text-[16px] text-emerald-500">
|
||||
terminal
|
||||
</span>
|
||||
<p className="text-sm font-medium">{t("setupGuideCommandMissingTitle")}</p>
|
||||
</div>
|
||||
<p className="text-xs text-text-muted">{t("setupGuideCommandMissingDesc")}</p>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
{/* CLI Fingerprint Matching */}
|
||||
<Card>
|
||||
<div className="flex items-center gap-3 mb-4">
|
||||
@@ -192,20 +270,7 @@ export default function AgentsPage() {
|
||||
<div className="flex flex-col gap-4">
|
||||
<p className="text-sm text-text-muted">{ts("cliFingerprintDesc")}</p>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{(
|
||||
[
|
||||
"codex",
|
||||
"claude",
|
||||
"github",
|
||||
"antigravity",
|
||||
"kiro",
|
||||
"cursor",
|
||||
"kimi-coding",
|
||||
"kilocode",
|
||||
"cline",
|
||||
"qwen",
|
||||
] as const
|
||||
).map((providerId) => {
|
||||
{CLI_COMPAT_PROVIDER_IDS.map((providerId) => {
|
||||
const providerMeta = Object.values(AI_PROVIDERS).find(
|
||||
(p: any) => p.id === providerId
|
||||
) as any;
|
||||
@@ -269,9 +334,13 @@ export default function AgentsPage() {
|
||||
: "bg-zinc-500/10 text-zinc-400"
|
||||
}`}
|
||||
>
|
||||
<span className="material-symbols-outlined text-[20px]">
|
||||
{agent.installed ? "smart_toy" : "block"}
|
||||
</span>
|
||||
{getAgentIconId(agent.id) ? (
|
||||
<ProviderIcon providerId={getAgentIconId(agent.id)!} size={20} type="color" />
|
||||
) : (
|
||||
<span className="material-symbols-outlined text-[20px]">
|
||||
{agent.installed ? "smart_toy" : "block"}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<div>
|
||||
<div className="font-semibold text-sm flex items-center gap-1.5">
|
||||
@@ -327,22 +396,18 @@ export default function AgentsPage() {
|
||||
</div>
|
||||
<div className="flex-1 min-w-0">
|
||||
<div className="flex items-center gap-2 mb-1">
|
||||
<h3 className="text-base font-semibold">OpenCode Integration</h3>
|
||||
<h3 className="text-base font-semibold">{t("opencodeIntegration")}</h3>
|
||||
<span className="text-[10px] px-1.5 py-0.5 rounded-full bg-emerald-500/10 text-emerald-600 dark:text-emerald-400 font-medium">
|
||||
opencode {agents.find((a) => a.id === "opencode")?.version} detected
|
||||
{t("opencodeDetected", {
|
||||
version: agents.find((a) => a.id === "opencode")?.version || "",
|
||||
})}
|
||||
</span>
|
||||
</div>
|
||||
<p className="text-sm text-text-muted mb-3">
|
||||
Generate a ready-to-use{" "}
|
||||
<code className="text-xs bg-black/[0.06] dark:bg-white/[0.08] px-1 py-0.5 rounded">
|
||||
opencode.json
|
||||
</code>{" "}
|
||||
with your OmniRoute base URL and all available models — drop it in your project root
|
||||
and run{" "}
|
||||
<code className="text-xs bg-black/[0.06] dark:bg-white/[0.08] px-1 py-0.5 rounded">
|
||||
opencode
|
||||
</code>
|
||||
.
|
||||
{t("opencodeDesc", {
|
||||
configFile: "opencode.json",
|
||||
command: "opencode",
|
||||
})}
|
||||
</p>
|
||||
<Button
|
||||
variant="secondary"
|
||||
@@ -399,7 +464,9 @@ export default function AgentsPage() {
|
||||
<span className="material-symbols-outlined text-[16px] mr-1">
|
||||
{opencodeConfigDone ? "check" : "download"}
|
||||
</span>
|
||||
{opencodeConfigDone ? "Downloaded!" : "Download opencode.json"}
|
||||
{opencodeConfigDone
|
||||
? t("downloaded")
|
||||
: t("downloadConfig", { file: "opencode.json" })}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -33,6 +33,16 @@ export default function CLIToolsPageClient({ machineId }) {
|
||||
const [apiKeys, setApiKeys] = useState([]);
|
||||
const [toolStatuses, setToolStatuses] = useState({});
|
||||
const [statusesLoaded, setStatusesLoaded] = useState(false);
|
||||
const translateOrFallback = useCallback(
|
||||
(key, fallback, values = undefined) => {
|
||||
try {
|
||||
return t(key, values);
|
||||
} catch {
|
||||
return fallback;
|
||||
}
|
||||
},
|
||||
[t]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
fetchConnections();
|
||||
@@ -291,8 +301,42 @@ export default function CLIToolsPageClient({ machineId }) {
|
||||
}
|
||||
};
|
||||
|
||||
const getToolDocsHref = (toolId, tool) => {
|
||||
if (typeof tool.docsUrl === "string" && tool.docsUrl.trim()) {
|
||||
return tool.docsUrl.trim();
|
||||
}
|
||||
return `/docs?section=cli-tools&tool=${toolId}`;
|
||||
};
|
||||
|
||||
const getToolUseCase = (toolId, tool) => {
|
||||
const fallbackDescription = translateOrFallback(`toolDescriptions.${toolId}`, tool.description);
|
||||
return translateOrFallback(`toolUseCases.${toolId}`, fallbackDescription);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-6">
|
||||
<Card>
|
||||
<div className="flex items-start gap-3">
|
||||
<div className="p-2 rounded-lg bg-primary/10 text-primary">
|
||||
<span className="material-symbols-outlined text-[20px]">tips_and_updates</span>
|
||||
</div>
|
||||
<div className="flex-1 min-w-0">
|
||||
<h2 className="text-sm font-semibold">{t("howItWorks")}</h2>
|
||||
<div className="mt-2 grid grid-cols-1 md:grid-cols-3 gap-2 text-xs text-text-muted">
|
||||
<div className="rounded-lg border border-border/50 bg-black/[0.02] dark:bg-white/[0.02] p-2.5">
|
||||
{t("installationGuide")}
|
||||
</div>
|
||||
<div className="rounded-lg border border-border/50 bg-black/[0.02] dark:bg-white/[0.02] p-2.5">
|
||||
{t("configureEndpoint")}
|
||||
</div>
|
||||
<div className="rounded-lg border border-border/50 bg-black/[0.02] dark:bg-white/[0.02] p-2.5">
|
||||
{t("testConnection")}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
{!hasActiveProviders && (
|
||||
<Card className="border-yellow-500/50 bg-yellow-500/5">
|
||||
<div className="flex items-center gap-3">
|
||||
@@ -308,7 +352,38 @@ export default function CLIToolsPageClient({ machineId }) {
|
||||
)}
|
||||
|
||||
<div className="flex flex-col gap-4">
|
||||
{Object.entries(CLI_TOOLS).map(([toolId, tool]) => renderToolCard(toolId, tool))}
|
||||
{Object.entries(CLI_TOOLS).map(([toolId, tool]) => {
|
||||
const docsHref = getToolDocsHref(toolId, tool);
|
||||
const isExternalDocs = /^https?:\/\//i.test(docsHref);
|
||||
return (
|
||||
<div key={toolId} className="flex flex-col gap-2.5">
|
||||
{renderToolCard(toolId, tool)}
|
||||
<div className="rounded-lg border border-border/50 bg-black/[0.02] dark:bg-white/[0.02] p-3">
|
||||
<div className="flex flex-col sm:flex-row sm:items-start sm:justify-between gap-2.5">
|
||||
<div className="min-w-0">
|
||||
<p className="text-[11px] font-semibold uppercase tracking-wide text-text-muted">
|
||||
{t("whenToUseLabel")}
|
||||
</p>
|
||||
<p className="text-xs text-text-muted mt-1 break-words">
|
||||
{getToolUseCase(toolId, tool)}
|
||||
</p>
|
||||
</div>
|
||||
<a
|
||||
href={docsHref}
|
||||
target={isExternalDocs ? "_blank" : undefined}
|
||||
rel={isExternalDocs ? "noopener noreferrer" : undefined}
|
||||
className="inline-flex items-center gap-1.5 text-xs text-primary hover:text-primary/80 transition-colors whitespace-nowrap"
|
||||
>
|
||||
<span className="material-symbols-outlined text-[14px]" aria-hidden="true">
|
||||
menu_book
|
||||
</span>
|
||||
{t("openToolDocs")}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -16,27 +16,18 @@ import Tooltip from "@/shared/components/Tooltip";
|
||||
import ModelRoutingSection from "@/shared/components/ModelRoutingSection";
|
||||
import { useCopyToClipboard } from "@/shared/hooks/useCopyToClipboard";
|
||||
import { useNotificationStore } from "@/store/notificationStore";
|
||||
import { ROUTING_STRATEGIES } from "@/shared/constants/routingStrategies";
|
||||
import { useTranslations } from "next-intl";
|
||||
|
||||
// Validate combo name: letters, numbers, -, _, /, .
|
||||
const VALID_NAME_REGEX = /^[a-zA-Z0-9_/.-]+$/;
|
||||
|
||||
const STRATEGY_OPTIONS = [
|
||||
{ value: "priority", labelKey: "priority", descKey: "priorityDesc", icon: "sort" },
|
||||
{ value: "weighted", labelKey: "weighted", descKey: "weightedDesc", icon: "percent" },
|
||||
{ value: "round-robin", labelKey: "roundRobin", descKey: "roundRobinDesc", icon: "autorenew" },
|
||||
{ value: "random", labelKey: "random", descKey: "randomDesc", icon: "shuffle" },
|
||||
{ value: "least-used", labelKey: "leastUsed", descKey: "leastUsedDesc", icon: "low_priority" },
|
||||
{ value: "cost-optimized", labelKey: "costOpt", descKey: "costOptimizedDesc", icon: "savings" },
|
||||
{
|
||||
value: "fill-first",
|
||||
labelKey: "fillFirst",
|
||||
descKey: "fillFirstDesc",
|
||||
icon: "stacked_bar_chart",
|
||||
},
|
||||
{ value: "p2c", labelKey: "p2c", descKey: "p2cDesc", icon: "compare_arrows" },
|
||||
{ value: "strict-random", labelKey: "strictRandom", descKey: "strictRandomDesc", icon: "casino" },
|
||||
];
|
||||
const STRATEGY_OPTIONS = ROUTING_STRATEGIES.map((strategy) => ({
|
||||
value: strategy.value,
|
||||
labelKey: strategy.labelKey,
|
||||
descKey: strategy.combosDescKey,
|
||||
icon: strategy.icon,
|
||||
}));
|
||||
|
||||
const STRATEGY_GUIDANCE_FALLBACK = {
|
||||
priority: {
|
||||
|
||||
@@ -134,7 +134,7 @@ export default function HealthPage() {
|
||||
);
|
||||
}
|
||||
|
||||
const { system, providerHealth, rateLimitStatus, lockouts } = data;
|
||||
const { system, providerHealth, providerSummary, rateLimitStatus, lockouts } = data;
|
||||
const cbEntries = Object.entries(providerHealth || {});
|
||||
const lockoutEntries = Object.entries(lockouts || {});
|
||||
|
||||
@@ -235,11 +235,37 @@ export default function HealthPage() {
|
||||
</div>
|
||||
<span className="text-sm text-text-muted">{t("providers")}</span>
|
||||
</div>
|
||||
<p className="text-xl font-semibold text-text-main">{cbEntries.length}</p>
|
||||
<p className="text-xs text-text-muted mt-1">
|
||||
{t("healthyCount", {
|
||||
count: cbEntries.filter(([, v]: [string, any]) => v.state === "CLOSED").length,
|
||||
<p className="text-xl font-semibold text-text-main">
|
||||
{providerSummary?.configuredCount ?? cbEntries.length}
|
||||
</p>
|
||||
<p
|
||||
className="text-[11px] text-text-muted mt-1 inline-flex items-center gap-1"
|
||||
title={t("configuredProvidersHint")}
|
||||
>
|
||||
{t("configuredProvidersLabel")}
|
||||
<span className="material-symbols-outlined text-[12px]" aria-hidden="true">
|
||||
help
|
||||
</span>
|
||||
</p>
|
||||
<p
|
||||
className="text-xs text-text-muted inline-flex items-center gap-1"
|
||||
title={t("activeProvidersHint")}
|
||||
>
|
||||
{t("activeProviders", { count: providerSummary?.activeCount ?? 0 })}
|
||||
<span className="material-symbols-outlined text-[12px]" aria-hidden="true">
|
||||
info
|
||||
</span>
|
||||
</p>
|
||||
<p
|
||||
className="text-xs text-text-muted inline-flex items-center gap-1"
|
||||
title={t("monitoredProvidersHint")}
|
||||
>
|
||||
{t("monitoredProviders", {
|
||||
count: providerSummary?.monitoredCount ?? cbEntries.length,
|
||||
})}
|
||||
<span className="material-symbols-outlined text-[12px]" aria-hidden="true">
|
||||
info
|
||||
</span>
|
||||
</p>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
@@ -358,6 +358,14 @@ function parseApiError(raw: any, statusCode: number): { message: string; isCrede
|
||||
return { message: String(msg), isCredentials };
|
||||
}
|
||||
|
||||
/** Format file size to human-readable string */
|
||||
function formatFileSize(bytes: number): string {
|
||||
if (bytes < 1024) return `${bytes} B`;
|
||||
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(0)} KB`;
|
||||
if (bytes < 1024 * 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
||||
return `${(bytes / (1024 * 1024 * 1024)).toFixed(2)} GB`;
|
||||
}
|
||||
|
||||
/** Render image result thumbnails */
|
||||
function ImageResults({ data }: { data: any }) {
|
||||
const images: Array<{ url?: string; b64_json?: string; revised_prompt?: string }> =
|
||||
@@ -427,7 +435,9 @@ export default function MediaPageClient() {
|
||||
const [speechFormat, setSpeechFormat] = useState("mp3");
|
||||
|
||||
// Transcription-specific
|
||||
const MAX_TRANSCRIPTION_FILE_SIZE = 4 * 1024 * 1024 * 1024; // 4 GB
|
||||
const [audioFile, setAudioFile] = useState<File | null>(null);
|
||||
const [fileSizeError, setFileSizeError] = useState<string | null>(null);
|
||||
|
||||
// Fix #390: Track which local providers (sdwebui, comfyui) are actually configured
|
||||
// so we can hide them when they haven't been set up in the providers page
|
||||
@@ -743,18 +753,35 @@ export default function MediaPageClient() {
|
||||
{/* Transcription: file upload */}
|
||||
{activeTab === "transcription" ? (
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-text-main mb-2">Audio File</label>
|
||||
<label className="block text-sm font-medium text-text-main mb-2">Audio / Video File</label>
|
||||
<input
|
||||
type="file"
|
||||
accept="audio/*,video/*"
|
||||
onChange={(e) => setAudioFile(e.target.files?.[0] ?? null)}
|
||||
onChange={(e) => {
|
||||
const file = e.target.files?.[0] ?? null;
|
||||
setFileSizeError(null);
|
||||
if (file && file.size > MAX_TRANSCRIPTION_FILE_SIZE) {
|
||||
setFileSizeError(`File too large (${formatFileSize(file.size)}). Maximum allowed: 4 GB.`);
|
||||
setAudioFile(null);
|
||||
e.target.value = "";
|
||||
return;
|
||||
}
|
||||
setAudioFile(file);
|
||||
}}
|
||||
className="w-full px-3 py-2 rounded-lg bg-surface border border-black/10 dark:border-white/10 text-text-main text-sm focus:outline-none focus:ring-2 focus:ring-primary/30 file:mr-3 file:py-1 file:px-3 file:rounded file:border-0 file:bg-primary/10 file:text-primary file:text-sm"
|
||||
/>
|
||||
{audioFile && (
|
||||
<p className="text-xs text-text-muted mt-1">
|
||||
{audioFile.name} ({(audioFile.size / 1024).toFixed(0)} KB)
|
||||
{fileSizeError && (
|
||||
<p className="text-xs text-red-400 mt-1 flex items-center gap-1">
|
||||
<span className="material-symbols-outlined text-[12px]">error</span>
|
||||
{fileSizeError}
|
||||
</p>
|
||||
)}
|
||||
{audioFile && !fileSizeError && (
|
||||
<p className="text-xs text-text-muted mt-1">
|
||||
{audioFile.name} ({formatFileSize(audioFile.size)})
|
||||
</p>
|
||||
)}
|
||||
<p className="text-[10px] text-text-muted/60 mt-1">Supports audio and video files up to 4 GB</p>
|
||||
</div>
|
||||
) : (
|
||||
/* Prompt / Text */
|
||||
|
||||
@@ -89,6 +89,14 @@ export default function OnboardingWizard() {
|
||||
|
||||
const handleSetPassword = async () => {
|
||||
if (skipSecurity) {
|
||||
// (#574) Explicitly disable requireLogin when skipping password setup
|
||||
try {
|
||||
await fetch("/api/settings/require-login", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ requireLogin: false }),
|
||||
});
|
||||
} catch {}
|
||||
handleNext();
|
||||
return;
|
||||
}
|
||||
@@ -176,6 +184,19 @@ export default function OnboardingWizard() {
|
||||
|
||||
const handleFinish = async () => {
|
||||
try {
|
||||
// (#574) If no password was set during wizard, disable requireLogin
|
||||
// to prevent the user from being locked out on the login page
|
||||
const settings = await fetch("/api/settings/require-login")
|
||||
.then((r) => r.json())
|
||||
.catch(() => ({}));
|
||||
if (!settings.hasPassword) {
|
||||
await fetch("/api/settings/require-login", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ requireLogin: false }),
|
||||
}).catch(() => {});
|
||||
}
|
||||
|
||||
await fetch("/api/settings", {
|
||||
method: "PATCH",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
|
||||
@@ -119,9 +119,9 @@ export default function SearchToolsClient() {
|
||||
} catch (err: any) {
|
||||
setDuration(Date.now() - start);
|
||||
if (err.name === "AbortError") {
|
||||
setError("Request timed out (15s)");
|
||||
setError(t("requestTimedOut", { seconds: 15 }));
|
||||
} else {
|
||||
setError(err.message || "Network error");
|
||||
setError(err?.message || t("networkError"));
|
||||
}
|
||||
} finally {
|
||||
setLoading(false);
|
||||
|
||||
@@ -33,6 +33,7 @@ interface SearchFormProps {
|
||||
|
||||
export default function SearchForm({ onSearch, loading, onCancel, providers }: SearchFormProps) {
|
||||
const t = useTranslations("search");
|
||||
const tc = useTranslations("common");
|
||||
const [query, setQuery] = useState("");
|
||||
const [provider, setProvider] = useState("auto");
|
||||
const [searchType, setSearchType] = useState("web");
|
||||
@@ -92,7 +93,7 @@ export default function SearchForm({ onSearch, loading, onCancel, providers }: S
|
||||
<textarea
|
||||
value={query}
|
||||
onChange={(e) => setQuery(e.target.value)}
|
||||
placeholder="Enter search query..."
|
||||
placeholder={t("queryPlaceholder")}
|
||||
className="w-full bg-surface border border-border rounded-lg p-2.5 text-sm text-text-main resize-none h-16 focus:outline-none focus:ring-2 focus:ring-primary/30"
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === "Enter" && !e.shiftKey) {
|
||||
@@ -114,7 +115,7 @@ export default function SearchForm({ onSearch, loading, onCancel, providers }: S
|
||||
value={provider}
|
||||
onChange={(e: any) => setProvider(e.target.value)}
|
||||
options={[
|
||||
{ value: "auto", label: "auto (cheapest)" },
|
||||
{ value: "auto", label: t("providerAuto") },
|
||||
...activeProviders.map((p) => ({
|
||||
value: p.id,
|
||||
label: p.name,
|
||||
@@ -131,8 +132,8 @@ export default function SearchForm({ onSearch, loading, onCancel, providers }: S
|
||||
value={searchType}
|
||||
onChange={(e: any) => setSearchType(e.target.value)}
|
||||
options={[
|
||||
{ value: "web", label: "web" },
|
||||
{ value: "news", label: "news" },
|
||||
{ value: "web", label: t("searchTypeWeb") },
|
||||
{ value: "news", label: t("searchTypeNews") },
|
||||
]}
|
||||
className="w-full"
|
||||
/>
|
||||
@@ -172,7 +173,7 @@ export default function SearchForm({ onSearch, loading, onCancel, providers }: S
|
||||
<input
|
||||
value={country}
|
||||
onChange={(e) => setCountry(e.target.value)}
|
||||
placeholder="any"
|
||||
placeholder={t("optionAny")}
|
||||
className="w-full bg-surface border border-border rounded-md px-2 py-1.5 text-xs text-text-main focus:outline-none"
|
||||
/>
|
||||
</div>
|
||||
@@ -181,7 +182,7 @@ export default function SearchForm({ onSearch, loading, onCancel, providers }: S
|
||||
<input
|
||||
value={language}
|
||||
onChange={(e) => setLanguage(e.target.value)}
|
||||
placeholder="any"
|
||||
placeholder={t("optionAny")}
|
||||
className="w-full bg-surface border border-border rounded-md px-2 py-1.5 text-xs text-text-main focus:outline-none"
|
||||
/>
|
||||
</div>
|
||||
@@ -192,11 +193,11 @@ export default function SearchForm({ onSearch, loading, onCancel, providers }: S
|
||||
value={timeRange}
|
||||
onChange={(e: any) => setTimeRange(e.target.value)}
|
||||
options={[
|
||||
{ value: "", label: "any" },
|
||||
{ value: "day", label: "Past day" },
|
||||
{ value: "week", label: "Past week" },
|
||||
{ value: "month", label: "Past month" },
|
||||
{ value: "year", label: "Past year" },
|
||||
{ value: "", label: t("optionAny") },
|
||||
{ value: "day", label: t("timeRangeDay") },
|
||||
{ value: "week", label: t("timeRangeWeek") },
|
||||
{ value: "month", label: t("timeRangeMonth") },
|
||||
{ value: "year", label: t("timeRangeYear") },
|
||||
]}
|
||||
className="w-full"
|
||||
/>
|
||||
@@ -209,7 +210,7 @@ export default function SearchForm({ onSearch, loading, onCancel, providers }: S
|
||||
<input
|
||||
value={domainInput}
|
||||
onChange={(e) => setDomainInput(e.target.value)}
|
||||
placeholder="example.com"
|
||||
placeholder={t("domainPlaceholder")}
|
||||
className="flex-1 bg-surface border border-border rounded-md px-2 py-1.5 text-xs text-text-main focus:outline-none"
|
||||
onKeyDown={(e) => e.key === "Enter" && addDomain("include")}
|
||||
/>
|
||||
@@ -244,7 +245,7 @@ export default function SearchForm({ onSearch, loading, onCancel, providers }: S
|
||||
<input
|
||||
value={excludeDomainInput}
|
||||
onChange={(e) => setExcludeDomainInput(e.target.value)}
|
||||
placeholder="example.com"
|
||||
placeholder={t("domainPlaceholder")}
|
||||
className="flex-1 bg-surface border border-border rounded-md px-2 py-1.5 text-xs text-text-main focus:outline-none"
|
||||
onKeyDown={(e) => e.key === "Enter" && addDomain("exclude")}
|
||||
/>
|
||||
@@ -274,9 +275,9 @@ export default function SearchForm({ onSearch, loading, onCancel, providers }: S
|
||||
value={safeSearch}
|
||||
onChange={(e: any) => setSafeSearch(e.target.value)}
|
||||
options={[
|
||||
{ value: "off", label: "Off" },
|
||||
{ value: "moderate", label: "Moderate" },
|
||||
{ value: "strict", label: "Strict" },
|
||||
{ value: "off", label: t("safeSearchOff") },
|
||||
{ value: "moderate", label: t("safeSearchModerate") },
|
||||
{ value: "strict", label: t("safeSearchStrict") },
|
||||
]}
|
||||
className="w-full"
|
||||
/>
|
||||
@@ -289,7 +290,7 @@ export default function SearchForm({ onSearch, loading, onCancel, providers }: S
|
||||
<div className="p-4 border-b border-border">
|
||||
{loading ? (
|
||||
<Button variant="danger" onClick={onCancel} className="w-full">
|
||||
Cancel
|
||||
{tc("cancel")}
|
||||
</Button>
|
||||
) : (
|
||||
<Button
|
||||
@@ -298,7 +299,7 @@ export default function SearchForm({ onSearch, loading, onCancel, providers }: S
|
||||
disabled={noProviders || !query.trim()}
|
||||
className="w-full"
|
||||
>
|
||||
Search
|
||||
{tc("search")}
|
||||
</Button>
|
||||
)}
|
||||
{noProviders && <p className="text-xs text-text-muted mt-2">{t("noSearchProviders")}</p>}
|
||||
|
||||