mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-07-28 10:52:10 +03:00
Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
69e9bd81e9 | ||
|
|
26f927f798 | ||
|
|
2042dcf991 |
31
CHANGELOG.md
31
CHANGELOG.md
@@ -0,0 +1,31 @@
|
||||
# Changelog
|
||||
|
||||
## [2.3.7] - 2026-03-12
|
||||
|
||||
### Fixed
|
||||
|
||||
- **Cline OAuth**: Add `decodeURIComponent` before base64 decode so URL-encoded auth codes from the callback URL are parsed correctly, fixing "invalid or expired authorization code" errors on remote (LAN IP) setups
|
||||
- **Cline OAuth**: `mapTokens` now populates `name = firstName + lastName || email` so Cline accounts show real user names instead of "Account #ID"
|
||||
- **OAuth account names**: All OAuth exchange flows (exchange, poll, poll-callback) now normalize `name = email` when name is missing, so every OAuth account shows its email as the display label in the Providers dashboard
|
||||
- **OAuth account names**: Removed sequential "Account N" fallback in `db/providers.ts` — accounts with no email/name now use a stable ID-based label via `getAccountDisplayName()` instead of a sequential number that changes when accounts are deleted
|
||||
|
||||
## [2.3.6] - 2026-03-12
|
||||
|
||||
### Fixed
|
||||
|
||||
- **Provider test batch**: Fixed Zod schema to accept `providerId: null` (frontend sends null for non-provider modes); was incorrectly returning "Invalid request" for all batch tests
|
||||
- **Provider test modal**: Fixed `[object Object]` display by normalizing API error objects to strings before rendering in `setTestResults` and `ProviderTestResultsView`
|
||||
- **i18n**: Added missing keys `cliTools.toolDescriptions.opencode`, `cliTools.toolDescriptions.kiro`, `cliTools.guides.opencode`, `cliTools.guides.kiro` to `en.json`
|
||||
- **i18n**: Synchronized 1111 missing keys across all 29 non-English language files using English values as fallbacks
|
||||
|
||||
## [2.3.5] - 2026-03-11
|
||||
|
||||
### Fixed
|
||||
|
||||
- **@swc/helpers**: Added permanent `postinstall` fix to copy `@swc/helpers` into the standalone app's `node_modules` — prevents MODULE_NOT_FOUND crash on global npm installs
|
||||
|
||||
## [2.3.4] - 2026-03-10
|
||||
|
||||
### Added
|
||||
|
||||
- Multiple provider integrations and dashboard improvements
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "omniroute",
|
||||
"version": "2.3.6",
|
||||
"version": "2.3.7",
|
||||
"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": {
|
||||
|
||||
@@ -226,6 +226,12 @@ export async function POST(
|
||||
exchangeTokens(provider, code, redirectUri, codeVerifier, state)
|
||||
);
|
||||
|
||||
// Normalize: if name is missing, use email or displayName as fallback so accounts
|
||||
// always show a real label (e.g. user@gmail.com) instead of "Account #abc123"
|
||||
if (!tokenData.name && (tokenData.email || tokenData.displayName)) {
|
||||
tokenData.name = tokenData.email || tokenData.displayName;
|
||||
}
|
||||
|
||||
// Upsert: update existing connection if same provider+email, else create new
|
||||
const expiresAt = tokenData.expiresIn
|
||||
? new Date(Date.now() + tokenData.expiresIn * 1000).toISOString()
|
||||
@@ -297,6 +303,11 @@ export async function POST(
|
||||
}
|
||||
|
||||
if (result.success) {
|
||||
// Normalize: if name is missing, use email as fallback display label
|
||||
if (!result.tokens.name && (result.tokens.email || result.tokens.displayName)) {
|
||||
result.tokens.name = result.tokens.email || result.tokens.displayName;
|
||||
}
|
||||
|
||||
// Upsert: update existing connection if same provider+email, else create new
|
||||
const expiresAt = result.tokens.expiresIn
|
||||
? new Date(Date.now() + result.tokens.expiresIn * 1000).toISOString()
|
||||
@@ -418,6 +429,11 @@ export async function POST(
|
||||
exchangeTokens(provider, params.code, redirectUri, codeVerifier, params.state)
|
||||
);
|
||||
|
||||
// Normalize: if name is missing, use email as fallback display label
|
||||
if (!tokenData.name && (tokenData.email || tokenData.displayName)) {
|
||||
tokenData.name = tokenData.email || tokenData.displayName;
|
||||
}
|
||||
|
||||
// Upsert: update existing connection if same provider+email, else create new
|
||||
const expiresAt = tokenData.expiresIn
|
||||
? new Date(Date.now() + tokenData.expiresIn * 1000).toISOString()
|
||||
|
||||
@@ -126,18 +126,16 @@ export async function createProviderConnection(data: JsonRecord) {
|
||||
return cleanNulls(merged);
|
||||
}
|
||||
|
||||
// Generate name
|
||||
// Generate name: prefer explicit name, then email, then a stable short-ID label.
|
||||
// Avoid sequential "Account N" — it reassigns when accounts are deleted/reordered.
|
||||
let connectionName = data.name || null;
|
||||
if (!connectionName && data.authType === "oauth") {
|
||||
if (data.email) {
|
||||
connectionName = data.email;
|
||||
} else {
|
||||
const count = db
|
||||
.prepare("SELECT COUNT(*) as cnt FROM provider_connections WHERE provider = ?")
|
||||
.get(data.provider) as JsonRecord | undefined;
|
||||
const cntValue = toNumberOrZero(toRecord(count).cnt);
|
||||
connectionName = `Account ${cntValue + 1}`;
|
||||
connectionName = data.email as string;
|
||||
} else if (data.displayName) {
|
||||
connectionName = data.displayName as string;
|
||||
}
|
||||
// Otherwise leave null — UI will fall back to getAccountDisplayName() → "Account #<id>"
|
||||
}
|
||||
|
||||
// Auto-increment priority
|
||||
|
||||
@@ -13,7 +13,14 @@ export const cline = {
|
||||
},
|
||||
exchangeToken: async (config, code, redirectUri) => {
|
||||
try {
|
||||
// Cline embeds tokens as base64-encoded JSON in the auth code.
|
||||
// The code may be URL-encoded when pasted from the callback URL.
|
||||
let base64 = code;
|
||||
try {
|
||||
base64 = decodeURIComponent(base64);
|
||||
} catch {
|
||||
/* already decoded */
|
||||
}
|
||||
const padding = 4 - (base64.length % 4);
|
||||
if (padding !== 4) {
|
||||
base64 += "=".repeat(padding);
|
||||
@@ -62,16 +69,23 @@ export const cline = {
|
||||
};
|
||||
}
|
||||
},
|
||||
mapTokens: (tokens) => ({
|
||||
accessToken: tokens.access_token,
|
||||
refreshToken: tokens.refresh_token,
|
||||
expiresIn: tokens.expires_at
|
||||
? Math.floor((new Date(tokens.expires_at).getTime() - Date.now()) / 1000)
|
||||
: 3600,
|
||||
email: tokens.email,
|
||||
providerSpecificData: {
|
||||
firstName: tokens.firstName,
|
||||
lastName: tokens.lastName,
|
||||
},
|
||||
}),
|
||||
mapTokens: (tokens) => {
|
||||
const firstName = tokens.firstName || "";
|
||||
const lastName = tokens.lastName || "";
|
||||
const fullName = [firstName, lastName].filter(Boolean).join(" ").trim();
|
||||
return {
|
||||
accessToken: tokens.access_token,
|
||||
refreshToken: tokens.refresh_token,
|
||||
expiresIn: tokens.expires_at
|
||||
? Math.floor((new Date(tokens.expires_at).getTime() - Date.now()) / 1000)
|
||||
: 3600,
|
||||
// Use full name if available, fallback to email so UI shows a real label
|
||||
name: fullName || tokens.email || null,
|
||||
email: tokens.email,
|
||||
providerSpecificData: {
|
||||
firstName: tokens.firstName,
|
||||
lastName: tokens.lastName,
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user