mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-07-26 09:52:11 +03:00
- Fix API_KEY_SECRET undefined crash with deterministic fallback - Add code_review_rate_limit parsing for Team/Plus/Pro plan support - Move feature-81 doc to completed/ - Bump version to v1.1.1
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -95,3 +95,4 @@ security-analysis/
|
||||
# Deploy workflow (contains sensitive VPS credentials)
|
||||
.agent/workflows/deploy.md
|
||||
clipr/
|
||||
app.log
|
||||
|
||||
14
CHANGELOG.md
14
CHANGELOG.md
@@ -7,6 +7,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
---
|
||||
|
||||
## [1.1.1] — 2026-02-22
|
||||
|
||||
> ### 🐛 Bugfix Release — API Key Creation & Codex Team Plan Quotas
|
||||
>
|
||||
> Fixes API key creation crash when `API_KEY_SECRET` is not set and adds Code Review rate limit window to Codex quota display.
|
||||
|
||||
### 🐛 Bug Fixes
|
||||
|
||||
- **API Key Creation** — Added deterministic fallback for `API_KEY_SECRET` to prevent `crypto.createHmac` crash when the environment variable is not configured. Keys created without the secret are insecure (warned at startup) but the application no longer crashes ([#108](https://github.com/diegosouzapw/OmniRoute/issues/108))
|
||||
- **Codex Code Review Quota** — Added parsing of the third rate limit window (`code_review_rate_limit`) from the ChatGPT usage API, supporting Plus/Pro/Team plan differences. The dashboard now displays all three quota bars: Session (5h), Weekly, and Code Review ([#106](https://github.com/diegosouzapw/OmniRoute/issues/106))
|
||||
|
||||
---
|
||||
|
||||
## [1.1.0] — 2026-02-21
|
||||
|
||||
> ### 🐛 Bugfix Release — OAuth Client Secret and Codex Business Quotas
|
||||
@@ -437,6 +450,7 @@ New environment variables:
|
||||
|
||||
---
|
||||
|
||||
[1.1.1]: https://github.com/diegosouzapw/OmniRoute/releases/tag/v1.1.1
|
||||
[1.0.7]: https://github.com/diegosouzapw/OmniRoute/releases/tag/v1.0.7
|
||||
[1.0.6]: https://github.com/diegosouzapw/OmniRoute/releases/tag/v1.0.6
|
||||
[1.0.5]: https://github.com/diegosouzapw/OmniRoute/releases/tag/v1.0.5
|
||||
|
||||
@@ -1009,7 +1009,7 @@ Se não quiser criar credenciais próprias agora, ainda é possível usar o flux
|
||||
|
||||
## 🛠️ Tech Stack
|
||||
|
||||
- **Runtime**: Node.js 20+
|
||||
- **Runtime**: Node.js 18–22 LTS (⚠️ Node.js 24+ is **not supported** — `better-sqlite3` native binaries are incompatible)
|
||||
- **Language**: TypeScript 5.9 — **100% TypeScript** across `src/` and `open-sse/` (v1.0.6)
|
||||
- **Framework**: Next.js 16 + React 19 + Tailwind CSS 4
|
||||
- **Database**: LowDB (JSON) + SQLite (domain state + proxy logs)
|
||||
|
||||
@@ -81,17 +81,26 @@ console.log(`
|
||||
\\____/|_| |_| |_|_| |_|_|_| \\_\\___/ \\__,_|\\__\\___|
|
||||
\x1b[0m`);
|
||||
|
||||
// ── Node.js version check ──────────────────────────────────
|
||||
const nodeMajor = parseInt(process.versions.node.split(".")[0], 10);
|
||||
if (nodeMajor >= 24) {
|
||||
console.warn(`\x1b[33m ⚠ Warning: You are running Node.js ${process.versions.node}.
|
||||
OmniRoute uses better-sqlite3, a native addon that does not yet
|
||||
have compatible prebuilt binaries for Node.js 24+.
|
||||
You may experience errors like "is not a valid Win32 application"
|
||||
or "NODE_MODULE_VERSION mismatch".
|
||||
|
||||
Recommended: use Node.js 22 LTS (or 20 LTS).
|
||||
Workaround: npm rebuild better-sqlite3\x1b[0m
|
||||
`);
|
||||
}
|
||||
|
||||
// ── Resolve server entry ───────────────────────────────────
|
||||
const serverJs = join(APP_DIR, "server.js");
|
||||
|
||||
if (!existsSync(serverJs)) {
|
||||
console.error(
|
||||
"\x1b[31m✖ Server not found at:\x1b[0m",
|
||||
serverJs,
|
||||
);
|
||||
console.error(
|
||||
" This usually means the package was not built correctly.",
|
||||
);
|
||||
console.error("\x1b[31m✖ Server not found at:\x1b[0m", serverJs);
|
||||
console.error(" This usually means the package was not built correctly.");
|
||||
console.error(" Try reinstalling: npm install -g omniroute");
|
||||
process.exit(1);
|
||||
}
|
||||
@@ -119,7 +128,10 @@ server.stdout.on("data", (data) => {
|
||||
process.stdout.write(text);
|
||||
|
||||
// Detect server ready
|
||||
if (!started && (text.includes("Ready") || text.includes("started") || text.includes("listening"))) {
|
||||
if (
|
||||
!started &&
|
||||
(text.includes("Ready") || text.includes("started") || text.includes("listening"))
|
||||
) {
|
||||
started = true;
|
||||
onReady();
|
||||
}
|
||||
|
||||
@@ -545,25 +545,48 @@ async function getCodexUsage(accessToken) {
|
||||
secondaryWindow.reset_at ? secondaryWindow.reset_at * 1000 : null
|
||||
);
|
||||
|
||||
// Parse code review rate limit (3rd window — differs per plan: Plus/Pro/Team)
|
||||
const codeReviewRateLimit = data.code_review_rate_limit || {};
|
||||
const codeReviewWindow = codeReviewRateLimit.primary_window || {};
|
||||
const codeReviewResetAt = parseResetTime(
|
||||
codeReviewWindow.reset_at ? codeReviewWindow.reset_at * 1000 : null
|
||||
);
|
||||
|
||||
const quotas: Record<string, any> = {
|
||||
session: {
|
||||
used: primaryWindow.used_percent || 0,
|
||||
total: 100,
|
||||
remaining: 100 - (primaryWindow.used_percent || 0),
|
||||
resetAt: sessionResetAt,
|
||||
unlimited: false,
|
||||
},
|
||||
weekly: {
|
||||
used: secondaryWindow.used_percent || 0,
|
||||
total: 100,
|
||||
remaining: 100 - (secondaryWindow.used_percent || 0),
|
||||
resetAt: weeklyResetAt,
|
||||
unlimited: false,
|
||||
},
|
||||
};
|
||||
|
||||
// Only include code review quota if the API returned data for it
|
||||
if (
|
||||
codeReviewWindow.used_percent !== undefined ||
|
||||
codeReviewWindow.remaining_count !== undefined
|
||||
) {
|
||||
quotas.code_review = {
|
||||
used: codeReviewWindow.used_percent || 0,
|
||||
total: 100,
|
||||
remaining: 100 - (codeReviewWindow.used_percent || 0),
|
||||
resetAt: codeReviewResetAt,
|
||||
unlimited: false,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
plan: data.plan_type || "unknown",
|
||||
limitReached: rateLimit.limit_reached || false,
|
||||
quotas: {
|
||||
session: {
|
||||
used: primaryWindow.used_percent || 0,
|
||||
total: 100,
|
||||
remaining: 100 - (primaryWindow.used_percent || 0),
|
||||
resetAt: sessionResetAt,
|
||||
unlimited: false,
|
||||
},
|
||||
weekly: {
|
||||
used: secondaryWindow.used_percent || 0,
|
||||
total: 100,
|
||||
remaining: 100 - (secondaryWindow.used_percent || 0),
|
||||
resetAt: weeklyResetAt,
|
||||
unlimited: false,
|
||||
},
|
||||
},
|
||||
quotas,
|
||||
};
|
||||
} catch (error) {
|
||||
throw new Error(`Failed to fetch Codex usage: ${error.message}`);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "omniroute",
|
||||
"version": "1.1.0",
|
||||
"version": "1.1.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": {
|
||||
@@ -17,7 +17,7 @@
|
||||
"open-sse"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
"node": ">=18.0.0 <24.0.0"
|
||||
},
|
||||
"keywords": [
|
||||
"ai",
|
||||
|
||||
@@ -4,7 +4,7 @@ import crypto from "crypto";
|
||||
if (!process.env.API_KEY_SECRET) {
|
||||
console.error("[SECURITY] API_KEY_SECRET is not set. API key CRC will be insecure.");
|
||||
}
|
||||
const API_KEY_SECRET = process.env.API_KEY_SECRET;
|
||||
const API_KEY_SECRET = process.env.API_KEY_SECRET || "omniroute-insecure-default-key";
|
||||
|
||||
/**
|
||||
* Generate 6-char random keyId
|
||||
|
||||
Reference in New Issue
Block a user