diff --git a/.gitignore b/.gitignore index 1842d3e974..4d23af7e97 100644 --- a/.gitignore +++ b/.gitignore @@ -95,3 +95,4 @@ security-analysis/ # Deploy workflow (contains sensitive VPS credentials) .agent/workflows/deploy.md clipr/ +app.log diff --git a/CHANGELOG.md b/CHANGELOG.md index 1c3cd65357..e0b660cea7 100644 --- a/CHANGELOG.md +++ b/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 diff --git a/README.md b/README.md index 8d4c466b82..bb546d2639 100644 --- a/README.md +++ b/README.md @@ -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) diff --git a/bin/omniroute.mjs b/bin/omniroute.mjs index a655e248d1..a8ab7d18da 100755 --- a/bin/omniroute.mjs +++ b/bin/omniroute.mjs @@ -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(); } diff --git a/open-sse/services/usage.ts b/open-sse/services/usage.ts index 406d0f7192..1aea556d34 100644 --- a/open-sse/services/usage.ts +++ b/open-sse/services/usage.ts @@ -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 = { + 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}`); diff --git a/package.json b/package.json index 1116ed6770..bb682bd8c5 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/src/shared/utils/apiKey.ts b/src/shared/utils/apiKey.ts index 41aa2f8a2f..6e30046817 100644 --- a/src/shared/utils/apiKey.ts +++ b/src/shared/utils/apiKey.ts @@ -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