Compare commits

..

24 Commits

Author SHA1 Message Date
diegosouzapw 70465ada4d feat(release): v1.8.1 — usage API proxy support
Build Electron Desktop App / Validate version (push) Failing after 35s
Build Electron Desktop App / Build Electron (macos-arm64) (push) Has been skipped
Build Electron Desktop App / Build Electron (linux) (push) Has been skipped
Build Electron Desktop App / Build Electron (macos-intel) (push) Has been skipped
Build Electron Desktop App / Build Electron (windows) (push) Has been skipped
Build Electron Desktop App / Create Release (push) Has been skipped
2026-03-03 12:06:42 -03:00
Diego Rodrigues de Sa e Souza 8ddea153d3 Merge pull request #195 from diegosouzapw/fix/issue-194-usage-proxy
fix: route usage API quota fetches through configured proxy (#194)
2026-03-03 12:05:55 -03:00
diegosouzapw 8dca8fba6b fix: route usage API quota fetches through configured proxy (#194) 2026-03-03 12:04:59 -03:00
diegosouzapw f21ba7df64 feat(release): v1.8.0 — empty tool_use.name validation, Windows electron fix
Build Electron Desktop App / Validate version (push) Failing after 31s
Build Electron Desktop App / Build Electron (macos-arm64) (push) Has been skipped
Build Electron Desktop App / Build Electron (linux) (push) Has been skipped
Build Electron Desktop App / Build Electron (macos-intel) (push) Has been skipped
Build Electron Desktop App / Build Electron (windows) (push) Has been skipped
Build Electron Desktop App / Create Release (push) Has been skipped
2026-03-03 11:21:31 -03:00
Diego Rodrigues de Sa e Souza ef917e42d1 Merge pull request #190 from benzntech/fix/electron-windows-collect-installers
fix: Windows electron release — collect portable exe by pattern
2026-03-03 11:19:53 -03:00
Diego Rodrigues de Sa e Souza 865a1b9b2c Merge pull request #193 from diegosouzapw/fix/issue-191-empty-tool-use-name
fix: validate empty tool_use.name to prevent Claude 400 errors (#191)
2026-03-03 11:19:45 -03:00
diegosouzapw de8a0836a8 fix: validate empty tool_use.name to prevent Claude 400 errors (#191) 2026-03-03 11:18:53 -03:00
benzntech b8272c55d7 fix: address review — break after first portable exe, remove debug ls 2026-03-03 09:27:31 +05:30
benzntech 8d93c13f9a fix: collect portable exe by pattern instead of hardcoded filename
electron-builder produces 'OmniRoute 1.6.9.exe' (with version) as the
portable exe, not 'OmniRoute.exe'. The hardcoded check failed, returning
exit code 1 and breaking every Windows build in the release workflow.

Now finds the portable exe by excluding 'Setup' (NSIS installer) and
blockmap files, then copies it as OmniRoute.exe for the release assets.
2026-03-03 09:20:24 +05:30
diegosouzapw 8152b030bf chore: bump version to 1.7.14 and update CHANGELOG
Build Electron Desktop App / Validate version (push) Failing after 37s
Build Electron Desktop App / Build Electron (macos-arm64) (push) Has been skipped
Build Electron Desktop App / Build Electron (linux) (push) Has been skipped
Build Electron Desktop App / Build Electron (macos-intel) (push) Has been skipped
Build Electron Desktop App / Build Electron (windows) (push) Has been skipped
Build Electron Desktop App / Create Release (push) Has been skipped
2026-03-02 19:18:38 -03:00
diegosouzapw 9352ac767f Merge PR #188: fix passthrough stream for Responses SSE (#186) 2026-03-02 19:17:38 -03:00
diegosouzapw 5f20029ff7 fix: make passthrough stream format-aware for Responses SSE (#186)
Passthrough mode now detects Responses SSE payloads (parsed.type starts
with 'response.') and skips Chat Completions-specific sanitization:
- sanitizeStreamingChunk() only runs on Chat Completions payloads
- fixInvalidId() and hasValuableContent() checks skipped for Responses
- Usage extraction still runs for both formats
- Content length tracking adapted for Responses delta format

This prevents potential stream corruption when Responses SSE data
triggers idFixed or other Chat Completions-specific rewrite conditions.
2026-03-02 19:13:34 -03:00
diegosouzapw dbd00117c8 chore: bump to 1.7.13 (npm republish)
Build Electron Desktop App / Validate version (push) Failing after 31s
Build Electron Desktop App / Build Electron (macos-arm64) (push) Has been skipped
Build Electron Desktop App / Build Electron (linux) (push) Has been skipped
Build Electron Desktop App / Build Electron (macos-intel) (push) Has been skipped
Build Electron Desktop App / Build Electron (windows) (push) Has been skipped
Build Electron Desktop App / Create Release (push) Has been skipped
2026-03-02 18:51:24 -03:00
diegosouzapw 2902a0fe26 chore: bump version to 1.7.12
Build Electron Desktop App / Validate version (push) Failing after 30s
Build Electron Desktop App / Build Electron (macos-arm64) (push) Has been skipped
Build Electron Desktop App / Build Electron (linux) (push) Has been skipped
Build Electron Desktop App / Build Electron (macos-intel) (push) Has been skipped
Build Electron Desktop App / Build Electron (windows) (push) Has been skipped
Build Electron Desktop App / Create Release (push) Has been skipped
2026-03-02 18:47:07 -03:00
diegosouzapw 7ba57634c1 feat: add blackbox.ai to dashboard frontend (#175)
- Added blackbox provider to APIKEY_PROVIDERS in providers.ts
- Added blackbox pricing entries in pricing.ts
- Added blackbox to ProviderId typedef in types.ts
- Added blackbox models endpoint config in models/route.ts
2026-03-02 18:46:48 -03:00
diegosouzapw 211dde25d0 chore: bump version to 1.7.11 and update CHANGELOG
Build Electron Desktop App / Validate version (push) Failing after 28s
Build Electron Desktop App / Build Electron (macos-arm64) (push) Has been skipped
Build Electron Desktop App / Build Electron (linux) (push) Has been skipped
Build Electron Desktop App / Build Electron (macos-intel) (push) Has been skipped
Build Electron Desktop App / Build Electron (windows) (push) Has been skipped
Build Electron Desktop App / Create Release (push) Has been skipped
2026-03-02 18:33:08 -03:00
diegosouzapw 57ff59aef2 Merge PR #183: fix projectId warnings + add blackbox.ai provider (#175, #176)
- Added warning logs when generateProjectId() is used as fallback
- Prefer translator-set body.project before generating a new fallback
- Added blackbox.ai as OpenAI-compatible provider with 6 models + logo
- Includes improvement from Copilot PRs #184 and #185
2026-03-02 18:32:08 -03:00
Diego Rodrigues de Sa e Souza c39faba2b5 Update open-sse/executors/antigravity.ts
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-03-02 18:22:49 -03:00
diegosouzapw 212bca2e1e fix: add projectId warning logs and blackbox.ai provider (#175, #176)
- Added warning logs when generateProjectId() is used as fallback
  in antigravity.ts and openai-to-gemini.ts (3 locations), helping
  admins diagnose 404 errors from fake GCP project IDs (#176)
- Added blackbox.ai as new OpenAI-compatible provider with 6 models
  (GPT-4o, Gemini 2.5 Flash, Claude Sonnet 4, DeepSeek V3,
  Blackbox AI, Blackbox AI Pro) and provider logo (#175)
2026-03-02 17:58:43 -03:00
Diego Rodrigues de Sa e Souza f807c56e31 Merge pull request #182 from diegosouzapw/dependabot/npm_and_yarn/development-51b319602c
deps: bump the development group with 2 updates
2026-03-02 17:47:43 -03:00
Diego Rodrigues de Sa e Souza 5510c25040 Merge pull request #181 from diegosouzapw/dependabot/npm_and_yarn/production-d7c3d31362
deps: bump wreq-js from 2.0.1 to 2.1.1 in the production group
2026-03-02 17:47:26 -03:00
dependabot[bot] 9d884d2d60 deps: bump the development group with 2 updates
Bumps the development group with 2 updates: [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) and [lint-staged](https://github.com/lint-staged/lint-staged).


Updates `@types/node` from 25.3.0 to 25.3.3
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

Updates `lint-staged` from 16.2.7 to 16.3.1
- [Release notes](https://github.com/lint-staged/lint-staged/releases)
- [Changelog](https://github.com/lint-staged/lint-staged/blob/main/CHANGELOG.md)
- [Commits](https://github.com/lint-staged/lint-staged/compare/v16.2.7...v16.3.1)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-version: 25.3.3
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: development
- dependency-name: lint-staged
  dependency-version: 16.3.1
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: development
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-02 20:22:45 +00:00
dependabot[bot] f26fa67374 deps: bump wreq-js from 2.0.1 to 2.1.1 in the production group
Bumps the production group with 1 update: [wreq-js](https://github.com/sqdshguy/wreq-js).


Updates `wreq-js` from 2.0.1 to 2.1.1
- [Release notes](https://github.com/sqdshguy/wreq-js/releases)
- [Commits](https://github.com/sqdshguy/wreq-js/compare/v2.0.1...v2.1.1)

---
updated-dependencies:
- dependency-name: wreq-js
  dependency-version: 2.1.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-02 20:22:16 +00:00
diegosouzapw ccb314e065 feat: update agent workflows to use PR-based flow with user verification
Refactor resolve-issues and review-prs workflows to create branches
and PRs instead of committing directly. Add mandatory stop points
for user verification before merging, closing issues, or releasing.
Includes deploy step to local VPS after release.
2026-03-02 16:37:17 -03:00
18 changed files with 290 additions and 119 deletions
+29 -16
View File
@@ -6,7 +6,7 @@ description: Fetch all open GitHub issues, analyze bugs, resolve what's possible
## Overview
This workflow fetches all open issues from the project's GitHub repository, classifies them, analyzes bugs, resolves what can be fixed, and triages issues with insufficient information. **It does NOT commit or release automatically** — it presents a report and waits for user validation before proceeding.
This workflow fetches all open issues from the project's GitHub repository, classifies them, analyzes bugs, resolves what can be fixed, and triages issues with insufficient information. **It does NOT merge or release automatically** — it creates a PR and waits for user validation before merging.
## Steps
@@ -60,11 +60,12 @@ Call the `/issue-triage` workflow (located at `~/.gemini/antigravity/global_work
Proceed with resolution:
1. **Research** — Search the codebase for files related to the issue
2. **Root Cause** — Identify the root cause by reading the relevant source files
3. **Implement Fix** — Apply the fix following existing code patterns and conventions
4. **Test** — Build the project and run tests to verify the fix
5. **DO NOT commit yet** — Leave changes staged but uncommitted
1. **Create a fix branch**`git checkout -b fix/issue-<NUMBER>-<short-description>`
2. **Research** — Search the codebase for files related to the issue
3. **Root Cause** — Identify the root cause by reading the relevant source files
4. **Implement Fix** — Apply the fix following existing code patterns and conventions
5. **Test** — Build the project and run tests to verify the fix
6. **Commit** — Commit with message format: `fix: <description> (#<issue_number>)`
### 5. Generate Report & Wait for Validation
@@ -83,25 +84,37 @@ Present a summary report to the user via `notify_user` with `BlockedOnUser: true
- If the user requests changes → Apply the requested adjustments first, then present the report again
- If the user rejects → Revert the changes and stop
### 6. Commit All Fixes (only after user approval)
### 6. Commit & Push Fix Branch (only after user approval)
After the user validates:
- Commit each fix individually with message format: `fix: <description> (#<issue_number>)`
- Each fix should be its own commit for clean git history
- Push the fix branch: `git push origin fix/issue-<NUMBER>-<short-description>`
- Create a PR: `gh pr create --title "fix: <description> (#<issue_number>)" --body "<details>" --base main`
### 7. Close Resolved Issues
### 7. 🛑 WAIT — Notify User & Await PR Verification
For each successfully fixed issue:
// turbo
**This is a mandatory stop point.** Use `notify_user` with `BlockedOnUser: true`:
- Close with a comment: `gh issue close <NUMBER> --repo <owner>/<repo> --comment "Fixed in <commit_hash>. The fix will be included in the next release."`
- Inform the user that the PR was created and is **awaiting their verification**
- Include the PR number, URL, and a summary of what was changed
- **DO NOT merge, close issues, generate releases, or deploy until the user confirms**
### 8. Update Docs & Release
Wait for the user to respond:
If any fixes were committed:
- **User confirms** → Proceed to step 8
- **User requests changes** → Apply changes, push to the same branch, notify again
- **User rejects** → Close the PR and stop
1. Run the `/update-docs` workflow (at `~/.gemini/antigravity/global_workflows/update-docs.md`) to update CHANGELOG and README
2. Run the `/generate-release` workflow (at `.agents/workflows/generate-release.md`) to bump version, tag, and publish
### 8. Merge, Close Issues & Release (only after user confirms PR)
After the user confirms the PR:
1. **Merge** the PR: `gh pr merge <NUMBER> --merge --repo <owner>/<repo>` or via local merge
2. **Close** resolved issues with a comment: `gh issue close <NUMBER> --repo <owner>/<repo> --comment "Fixed in <commit_hash>. The fix will be included in the next release."`
3. **Switch to main**: `git checkout main && git pull`
4. Run the `/update-docs` workflow (at `~/.gemini/antigravity/global_workflows/update-docs.md`) to update CHANGELOG and README
5. Run the `/generate-release` workflow (at `.agents/workflows/generate-release.md`) to bump version, tag, and publish
6. Deploy to local VPS: `ssh root@192.168.0.15 "npm install -g omniroute@<VERSION> && pm2 restart omniroute"`
If NO fixes were committed, skip this step and just present the report.
+35 -11
View File
@@ -6,7 +6,7 @@ description: Analyze open Pull Requests from the project's GitHub repository, ge
## Overview
This workflow fetches all open PRs from the project's GitHub repository, performs a critical analysis of each one, generates a detailed report, and waits for user approval before proceeding with implementation.
This workflow fetches all open PRs from the project's GitHub repository, performs a critical analysis of each one, generates a detailed report, and waits for user approval before proceeding with implementation. **All improvements are committed on top of the PR branch** and the user must verify before merge.
## Steps
@@ -94,28 +94,52 @@ Perform a **global impact assessment** to verify whether the PR changes are comp
### 6. Implementation (if approved)
- Checkout the PR branch or apply changes locally
- Checkout the PR branch: `gh pr checkout <NUMBER>`
- Implement any required fixes identified in the analysis
- If the Cross-Layer Analysis (3f) identified missing frontend/backend counterparts, implement them in this step
- If the Cross-Layer Analysis (3f) identified missing frontend/backend counterparts, implement them
- **Commit improvements on top of the PR branch** with descriptive commit messages
- Run the project's test suite to verify nothing breaks
// turbo
- Run: `npm test` or equivalent test command
- Build the project to verify compilation
// turbo
- Run: `npm run build` or equivalent build command
- If all checks pass, prepare the merge
- Push the updated branch: `git push origin <branch-name>`
### 7. Thank the Contributor
### 7. 🛑 WAIT — Notify User & Await PR Verification
- After the PR is approved (and before or after merging), post a **thank-you comment** on the PR via the GitHub UI or API
**This is a mandatory stop point.** Use `notify_user` with `BlockedOnUser: true`:
- Inform the user that the PR has been **improved and pushed**, and is **awaiting their verification**
- Include:
- PR number and URL
- Summary of improvements/fixes applied
- Build/test status
- List of files changed
- **DO NOT merge, generate releases, or deploy until the user confirms**
Wait for the user to respond:
- **User confirms** → Proceed to step 8
- **User requests more changes** → Apply changes, push to the same branch, notify again
- **User rejects** → Leave a review comment and stop
### 8. Thank the Contributor
- Post a **thank-you comment** on the PR via the GitHub API
- The message should:
- Thank the author by name/username for their contribution
- Briefly mention what the PR accomplishes
- Briefly mention what the PR accomplishes and any improvements applied
- Be friendly, professional, and encouraging
- Example: _"Thanks @author for this great contribution! 🎉 The [feature/fix] is now merged and will be part of the next release. We appreciate your effort!"_
### 8. Post-Merge (if applicable)
### 9. Merge & Release (only after user confirms PR)
- Update CHANGELOG.md with the new feature
- Consider version bump if warranted
- Follow the `/generate-release` workflow if a release is needed
After the user confirms the PR:
1. **Merge** the PR into main (local merge with `--no-ff` or via `gh pr merge`)
2. **Push** to main: `git push origin main`
3. **Clean up** the feature branch: `git branch -d <branch-name>`
4. **Update CHANGELOG.md** with the new feature/fix
5. Run the `/generate-release` workflow (at `.agents/workflows/generate-release.md`) to bump version, tag, and publish
6. Deploy to local VPS: `ssh root@192.168.0.15 "npm install -g omniroute@<VERSION> && pm2 restart omniroute"`
+6 -2
View File
@@ -110,9 +110,13 @@ jobs:
for file in *${{ matrix.ext }}; do
[ -f "$file" ] && cp "$file" ../../release-assets/
done
# Windows: also copy portable standalone exe
# Windows: also copy portable standalone exe as OmniRoute.exe
if [ "${{ matrix.platform }}" = "windows" ]; then
[ -f "OmniRoute.exe" ] && cp OmniRoute.exe ../../release-assets/
for file in *.exe; do
# Skip the NSIS installer (contains "Setup")
case "$file" in *Setup*) continue ;; esac
[ -f "$file" ] && cp "$file" "../../release-assets/OmniRoute.exe" && break
done
fi
- name: Upload artifacts
+33
View File
@@ -7,6 +7,39 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
---
## [1.8.1] — 2026-03-03
### 🐛 Bug Fixes
- **Usage API Proxy Support** — Quota/usage fetch calls (`/api/usage/[connectionId]`) now route through the dashboard-configured proxy (Global → Provider → Key level). Previously, usage fetchers used bare `fetch()` which bypassed the Global Proxy setting, causing "fetch failed" errors in Docker deployments behind a proxy. Fixes #194
## [1.8.0] — 2026-03-03
### 🐛 Bug Fixes
- **Empty `tool_use.name` Validation** — Fixed intermittent HTTP 400 errors when using Claude Code through OmniRoute. Assistant messages with empty `tool_use.name` fields (from interrupted tool calls or malformed history) are now validated and filtered at two layers: the `openai-to-claude` request translator and the `prepareClaudeRequest` sanitizer. Fixes #191
- **Windows Electron Release** — Fixed the "Collect installers" step failing in every Windows build since v1.7.5+. `electron-builder` produces versioned portable exe filenames (e.g., `OmniRoute 1.6.9.exe`), not the hardcoded `OmniRoute.exe` the workflow expected. Now finds the portable exe dynamically by pattern. PR #190 by @benzntech
## [1.7.14] — 2026-03-02
### 🐛 Bug Fixes
- **Responses SSE Passthrough** — Passthrough mode is now format-aware: Responses SSE payloads (`response.*` type) skip Chat Completions-specific sanitization (`sanitizeStreamingChunk`, `fixInvalidId`, `hasValuableContent`), preventing potential stream corruption for Responses-native clients. Usage extraction still works for both formats. Fixes #186
### ✨ Features
- **Blackbox AI Dashboard** — Added blackbox.ai provider to the dashboard frontend (providers page, pricing, models endpoint). Completes #175
## [1.7.11] — 2026-03-02
### ✨ Features
- **Blackbox AI Provider** — Added blackbox.ai as a new OpenAI-compatible provider with 6 default models (GPT-4o, Gemini 2.5 Flash, Claude Sonnet 4, DeepSeek V3, Blackbox AI, Blackbox AI Pro) and provider logo. Fixes #175
### 🐛 Bug Fixes
- **Antigravity 404 Error** — Added warning logs when `generateProjectId()` generates a fallback project ID because `credentials.projectId` is null. The executor now prefers the translator-set `body.project` before generating a new fallback, eliminating duplicate warnings and ID mismatch. Fixes #176. Includes improvements from PRs #184 and #185
## [1.7.10] — 2026-03-02
### 🐛 Bug Fixes
+19
View File
@@ -641,6 +641,25 @@ export const REGISTRY: Record<string, RegistryEntry> = {
],
},
blackbox: {
id: "blackbox",
alias: "bb",
format: "openai",
executor: "default",
baseUrl: "https://api.blackbox.ai/v1/chat/completions",
modelsUrl: "https://api.blackbox.ai/v1/models",
authType: "apikey",
authHeader: "bearer",
models: [
{ id: "gpt-4o", name: "GPT-4o" },
{ id: "gemini-2.5-flash", name: "Gemini 2.5 Flash" },
{ id: "claude-sonnet-4", name: "Claude Sonnet 4" },
{ id: "deepseek-v3", name: "DeepSeek V3" },
{ id: "blackboxai", name: "Blackbox AI" },
{ id: "blackboxai-pro", name: "Blackbox AI Pro" },
],
},
xai: {
id: "xai",
alias: "xai",
+12 -1
View File
@@ -36,7 +36,18 @@ export class AntigravityExecutor extends BaseExecutor {
}
transformRequest(model, body, stream, credentials) {
const projectId = credentials?.projectId || this.generateProjectId();
const bodyProjectId = body?.project;
const credentialsProjectId = credentials?.projectId;
const hasExplicitProject = !!(bodyProjectId || credentialsProjectId);
const projectId = bodyProjectId || credentialsProjectId || this.generateProjectId();
if (!hasExplicitProject) {
console.warn(
`[Antigravity] ⚠️ No projectId provided via body or credentials — using generated fallback "${projectId}". ` +
`This may cause 404 errors if the account has no active GCP project. ` +
`Ensure the OAuth token includes a valid project or the request includes a project field.`
);
}
// Fix contents for Claude models via Antigravity
const normalizedContents =
@@ -126,6 +126,15 @@ export function prepareClaudeRequest(body, provider = null) {
}
}
// Pass 1.4: Filter out tool_use blocks with empty names (causes Claude 400 error)
for (const msg of filtered) {
if (msg.role === "assistant" && Array.isArray(msg.content)) {
msg.content = msg.content.filter(
(block) => block.type !== "tool_use" || (block.name && block.name.trim())
);
}
}
// Pass 1.5: Fix tool_use/tool_result ordering
// Each tool_use must have tool_result in the NEXT message (not same message with other content)
filtered = fixToolUseOrdering(filtered);
@@ -228,7 +228,10 @@ function getContentBlocksFromMessage(msg, toolNameMap = new Map()) {
});
} else if (part.type === "tool_use") {
// Tool name already has prefix from tool declarations, keep as-is
blocks.push({ type: "tool_use", id: part.id, name: part.name, input: part.input });
// CRITICAL: Skip tool_use blocks with empty name (causes Claude 400 error)
if (part.name && part.name.trim()) {
blocks.push({ type: "tool_use", id: part.id, name: part.name, input: part.input });
}
}
}
} else if (msg.content) {
@@ -241,8 +244,12 @@ function getContentBlocksFromMessage(msg, toolNameMap = new Map()) {
if (msg.tool_calls && Array.isArray(msg.tool_calls)) {
for (const tc of msg.tool_calls) {
if (tc.type === "function") {
// CRITICAL: Skip tool_calls with empty function name (causes Claude 400 error)
const fnName = tc.function?.name;
if (!fnName || !fnName.trim()) continue;
// Apply prefix to tool name
const toolName = CLAUDE_OAUTH_TOOL_PREFIX + tc.function.name;
const toolName = CLAUDE_OAUTH_TOOL_PREFIX + fnName;
blocks.push({
type: "tool_use",
id: tc.id,
@@ -271,8 +271,16 @@ export function openaiToGeminiCLIRequest(model, body, stream) {
// Wrap Gemini CLI format in Cloud Code wrapper
function wrapInCloudCodeEnvelope(model, geminiCLI, credentials = null, isAntigravity = false) {
const hasRealProject = !!credentials?.projectId;
const projectId = credentials?.projectId || generateProjectId();
if (!hasRealProject) {
console.warn(
`[${isAntigravity ? "Antigravity" : "GeminiCLI"}] ⚠️ No projectId in credentials — using generated fallback "${projectId}". ` +
`This may cause 404 errors. Ensure the OAuth token includes a valid GCP project.`
);
}
const cleanModel = model.includes("/") ? model.split("/").pop()! : model;
const envelope: Record<string, any> = {
@@ -315,10 +323,17 @@ function wrapInCloudCodeEnvelope(model, geminiCLI, credentials = null, isAntigra
return envelope;
}
// Wrap Claude format in Cloud Code envelope for Antigravity
function wrapInCloudCodeEnvelopeForClaude(model, claudeRequest, credentials = null) {
const hasRealProject = !!credentials?.projectId;
const projectId = credentials?.projectId || generateProjectId();
if (!hasRealProject) {
console.warn(
`[Antigravity/Claude] ⚠️ No projectId in credentials — using generated fallback "${projectId}". ` +
`This may cause 404 errors. Ensure the OAuth token includes a valid GCP project.`
);
}
const cleanModel = model.includes("/") ? model.split("/").pop()! : model;
const envelope: Record<string, any> = {
+60 -40
View File
@@ -136,51 +136,71 @@ export function createSSEStream(options: any = {}) {
try {
let parsed = JSON.parse(trimmed.slice(5).trim());
// Sanitize: strip non-standard fields for OpenAI SDK compatibility
parsed = sanitizeStreamingChunk(parsed);
// Detect Responses SSE payloads (have a `type` field like "response.created",
// "response.output_item.added", etc.) and skip Chat Completions-specific
// sanitization to avoid corrupting the stream for Responses-native clients.
const isResponsesSSE =
parsed.type &&
typeof parsed.type === "string" &&
parsed.type.startsWith("response.");
const idFixed = fixInvalidId(parsed);
if (!hasValuableContent(parsed, FORMATS.OPENAI)) {
continue;
}
const delta = parsed.choices?.[0]?.delta;
// Extract <think> tags from streaming content
if (delta?.content && typeof delta.content === "string") {
const { content, thinking } = extractThinkingFromContent(delta.content);
delta.content = content;
if (thinking && !delta.reasoning_content) {
delta.reasoning_content = thinking;
if (isResponsesSSE) {
// Responses SSE: only extract usage, forward payload as-is
const extracted = extractUsage(parsed);
if (extracted) {
usage = extracted;
}
}
// Track content length from Responses format
if (parsed.delta && typeof parsed.delta === "string") {
totalContentLength += parsed.delta.length;
}
} else {
// Chat Completions: full sanitization pipeline
parsed = sanitizeStreamingChunk(parsed);
const content = delta?.content || delta?.reasoning_content;
if (content && typeof content === "string") {
totalContentLength += content.length;
}
const idFixed = fixInvalidId(parsed);
const extracted = extractUsage(parsed);
if (extracted) {
usage = extracted;
}
if (!hasValuableContent(parsed, FORMATS.OPENAI)) {
continue;
}
const isFinishChunk = parsed.choices?.[0]?.finish_reason;
if (isFinishChunk && !hasValidUsage(parsed.usage)) {
const estimated = estimateUsage(body, totalContentLength, FORMATS.OPENAI);
parsed.usage = filterUsageForFormat(estimated, FORMATS.OPENAI);
output = `data: ${JSON.stringify(parsed)}\n`;
usage = estimated;
injectedUsage = true;
} else if (isFinishChunk && usage) {
const buffered = addBufferToUsage(usage);
parsed.usage = filterUsageForFormat(buffered, FORMATS.OPENAI);
output = `data: ${JSON.stringify(parsed)}\n`;
injectedUsage = true;
} else if (idFixed) {
output = `data: ${JSON.stringify(parsed)}\n`;
injectedUsage = true;
const delta = parsed.choices?.[0]?.delta;
// Extract <think> tags from streaming content
if (delta?.content && typeof delta.content === "string") {
const { content, thinking } = extractThinkingFromContent(delta.content);
delta.content = content;
if (thinking && !delta.reasoning_content) {
delta.reasoning_content = thinking;
}
}
const content = delta?.content || delta?.reasoning_content;
if (content && typeof content === "string") {
totalContentLength += content.length;
}
const extracted = extractUsage(parsed);
if (extracted) {
usage = extracted;
}
const isFinishChunk = parsed.choices?.[0]?.finish_reason;
if (isFinishChunk && !hasValidUsage(parsed.usage)) {
const estimated = estimateUsage(body, totalContentLength, FORMATS.OPENAI);
parsed.usage = filterUsageForFormat(estimated, FORMATS.OPENAI);
output = `data: ${JSON.stringify(parsed)}\n`;
usage = estimated;
injectedUsage = true;
} else if (isFinishChunk && usage) {
const buffered = addBufferToUsage(usage);
parsed.usage = filterUsageForFormat(buffered, FORMATS.OPENAI);
output = `data: ${JSON.stringify(parsed)}\n`;
injectedUsage = true;
} else if (idFixed) {
output = `data: ${JSON.stringify(parsed)}\n`;
injectedUsage = true;
}
}
} catch {}
}
+24 -41
View File
@@ -1,12 +1,12 @@
{
"name": "omniroute",
"version": "1.7.10",
"version": "1.8.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "omniroute",
"version": "1.7.10",
"version": "1.8.0",
"hasInstallScript": true,
"license": "MIT",
"workspaces": [
@@ -3063,9 +3063,9 @@
"license": "MIT"
},
"node_modules/@types/node": {
"version": "25.3.0",
"resolved": "https://registry.npmjs.org/@types/node/-/node-25.3.0.tgz",
"integrity": "sha512-4K3bqJpXpqfg2XKGK9bpDTc6xO/xoUP/RBWS7AtRMug6zZFaRekiLzjVtAoZMquxoAbzBvy5nxQ7veS5eYzf8A==",
"version": "25.3.3",
"resolved": "https://registry.npmjs.org/@types/node/-/node-25.3.3.tgz",
"integrity": "sha512-DpzbrH7wIcBaJibpKo9nnSQL0MTRdnWttGyE5haGwK86xgMOkFLp7vEyfQPGLOJh5wNYiJ3V9PmUMDhV9u8kkQ==",
"license": "MIT",
"dependencies": {
"undici-types": "~7.18.0"
@@ -7763,19 +7763,18 @@
}
},
"node_modules/lint-staged": {
"version": "16.2.7",
"resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-16.2.7.tgz",
"integrity": "sha512-lDIj4RnYmK7/kXMya+qJsmkRFkGolciXjrsZ6PC25GdTfWOAWetR0ZbsNXRAj1EHHImRSalc+whZFg56F5DVow==",
"version": "16.3.1",
"resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-16.3.1.tgz",
"integrity": "sha512-bqvvquXzFBAlSbluugR4KXAe4XnO/QZcKVszpkBtqLWa2KEiVy8n6Xp38OeUbv/gOJOX4Vo9u5pFt/ADvbm42Q==",
"dev": true,
"license": "MIT",
"dependencies": {
"commander": "^14.0.2",
"commander": "^14.0.3",
"listr2": "^9.0.5",
"micromatch": "^4.0.8",
"nano-spawn": "^2.0.0",
"pidtree": "^0.6.0",
"string-argv": "^0.3.2",
"yaml": "^2.8.1"
"tinyexec": "^1.0.2",
"yaml": "^2.8.2"
},
"bin": {
"lint-staged": "bin/lint-staged.js"
@@ -8084,19 +8083,6 @@
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
"license": "MIT"
},
"node_modules/nano-spawn": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/nano-spawn/-/nano-spawn-2.0.0.tgz",
"integrity": "sha512-tacvGzUY5o2D8CBh2rrwxyNojUsZNU2zjNTzKQrkgGJQTbGAfArVWXSKMBokBeeg6C7OLRGUEyoFlYbfeWQIqw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=20.17"
},
"funding": {
"url": "https://github.com/sindresorhus/nano-spawn?sponsor=1"
}
},
"node_modules/nanoid": {
"version": "3.3.11",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
@@ -8730,19 +8716,6 @@
"url": "https://github.com/sponsors/jonschlinkert"
}
},
"node_modules/pidtree": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.6.0.tgz",
"integrity": "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==",
"dev": true,
"license": "MIT",
"bin": {
"pidtree": "bin/pidtree.js"
},
"engines": {
"node": ">=0.10"
}
},
"node_modules/pino": {
"version": "10.3.1",
"resolved": "https://registry.npmjs.org/pino/-/pino-10.3.1.tgz",
@@ -10384,6 +10357,16 @@
"integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==",
"license": "MIT"
},
"node_modules/tinyexec": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.2.tgz",
"integrity": "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=18"
}
},
"node_modules/tinyglobby": {
"version": "0.2.15",
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz",
@@ -11100,9 +11083,9 @@
"license": "ISC"
},
"node_modules/wreq-js": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/wreq-js/-/wreq-js-2.0.1.tgz",
"integrity": "sha512-7GcZpzVtRX/A5pqu2QXTcqKTiYJZZRIRiosJ94GT1+taQivysZmjvkwzk8IvLz1oE1P1rGWG8ujDLCYxusM1aQ==",
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/wreq-js/-/wreq-js-2.1.1.tgz",
"integrity": "sha512-nJBOMBTczqcyHpF8a8YdPyxb30htK2RxuAfr6O8a6oyKHj2nRPjXbZcGXrquIdZx1b+6NV/GHweD3OqWwE7n4A==",
"cpu": [
"x64",
"arm64"
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "omniroute",
"version": "1.7.10",
"version": "1.8.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": {
Binary file not shown.

After

Width:  |  Height:  |  Size: 441 KiB

@@ -143,6 +143,14 @@ const PROVIDER_MODELS_CONFIG = {
authPrefix: "Bearer ",
parseResponse: (data) => data.data || data.models || [],
},
blackbox: {
url: "https://api.blackbox.ai/v1/models",
method: "GET",
headers: { "Content-Type": "application/json" },
authHeader: "Authorization",
authPrefix: "Bearer ",
parseResponse: (data) => data.data || data.models || [],
},
xai: {
url: "https://api.x.ai/v1/models",
method: "GET",
+9 -3
View File
@@ -1,8 +1,9 @@
import { getProviderConnectionById, updateProviderConnection } from "@/lib/localDb";
import { getProviderConnectionById, updateProviderConnection, resolveProxyForConnection } from "@/lib/localDb";
import { getMachineId } from "@/shared/utils/machine";
import { getUsageForProvider } from "@omniroute/open-sse/services/usage.ts";
import { getExecutor } from "@omniroute/open-sse/executors/index.ts";
import { syncToCloud } from "@/lib/cloudSync";
import { runWithProxyContext } from "@omniroute/open-sse/utils/proxyFetch.ts";
/**
* Sync to cloud if enabled
@@ -139,8 +140,13 @@ export async function GET(request: Request, { params }: { params: Promise<{ conn
);
}
// Fetch usage from provider API
const usage = await getUsageForProvider(connection);
// Resolve proxy for this connection (key → combo → provider → global → direct)
const proxyInfo = await resolveProxyForConnection(connectionId);
// Fetch usage from provider API, wrapped in proxy context
const usage = await runWithProxyContext(proxyInfo?.proxy || null, () =>
getUsageForProvider(connection)
);
return Response.json(usage);
} catch (error) {
console.error("[Usage API] Error fetching usage:", error);
+1 -1
View File
@@ -8,7 +8,7 @@
*/
/**
* @typedef {'openai'|'claude'|'gemini'|'codex'|'qwen'|'deepseek'|'cohere'|'groq'|'mistral'|'openrouter'} ProviderId
* @typedef {'openai'|'claude'|'gemini'|'codex'|'qwen'|'deepseek'|'cohere'|'groq'|'blackbox'|'mistral'|'openrouter'} ProviderId
*/
/**
+10
View File
@@ -528,6 +528,16 @@ export const DEFAULT_PRICING = {
"qwen/qwen3-32b": { input: 0, output: 0, cached: 0, reasoning: 0, cache_creation: 0 },
},
// Blackbox AI
blackbox: {
"gpt-4o": { input: 0, output: 0, cached: 0, reasoning: 0, cache_creation: 0 },
"gemini-2.5-flash": { input: 0, output: 0, cached: 0, reasoning: 0, cache_creation: 0 },
"claude-sonnet-4": { input: 0, output: 0, cached: 0, reasoning: 0, cache_creation: 0 },
"deepseek-v3": { input: 0, output: 0, cached: 0, reasoning: 0, cache_creation: 0 },
blackboxai: { input: 0, output: 0, cached: 0, reasoning: 0, cache_creation: 0 },
"blackboxai-pro": { input: 0, output: 0, cached: 0, reasoning: 0, cache_creation: 0 },
},
// Fireworks
fireworks: {
"accounts/fireworks/models/gpt-oss-120b": {
+9
View File
@@ -145,6 +145,15 @@ export const APIKEY_PROVIDERS = {
textIcon: "GQ",
website: "https://groq.com",
},
blackbox: {
id: "blackbox",
alias: "bb",
name: "Blackbox AI",
icon: "view_in_ar",
color: "#1A1A2E",
textIcon: "BB",
website: "https://blackbox.ai",
},
xai: {
id: "xai",
alias: "xai",