fix(issues): API key creation crash (#108) & Codex code review quota (#106)

- 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:
diegosouzapw
2026-02-22 16:28:12 -03:00
parent f8a401da4b
commit 5dab4058c8
7 changed files with 78 additions and 28 deletions

1
.gitignore vendored
View File

@@ -95,3 +95,4 @@ security-analysis/
# Deploy workflow (contains sensitive VPS credentials)
.agent/workflows/deploy.md
clipr/
app.log

View File

@@ -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

View File

@@ -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 1822 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)

View File

@@ -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();
}

View File

@@ -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}`);

View File

@@ -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",

View File

@@ -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