[Part 1/3]refactor(qwen): replace legacy Qwen Code and remove OAuth provider (#7866)

* refactor(cli): remove legacy Qwen Code integration

* refactor(qwen): remove deprecated Qwen OAuth provider

* feat(cli): rebuild Qwen Code integration for upstream V4

* fix(qwen): clear stale CLI auth on reset

* test(qwen): align retired provider coverage

* fix(db): renumber qwen-cleanup migration 129 -> 130

release/v3.8.49 tip took slot 129 via #7843 (usage_history_codex_strong_identity,
itself renumbered from 128 during the #7838/#7840 base-red cleanup) after this
branch forked; renumber remove_unregistered_qwen_data to 130.

Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>

---------

Co-authored-by: Diego Rodrigues de Sa e Souza <8016841+diegosouzapw@users.noreply.github.com>
This commit is contained in:
backryun
2026-07-21 05:53:04 +09:00
committed by GitHub
parent 470e0811fd
commit 65e0aeda79
218 changed files with 1449 additions and 2347 deletions

View File

@@ -895,9 +895,6 @@ CODEX_OAUTH_CLIENT_ID=app_EMoamEEZ73f0CkXaXp7hrann
# ANTIGRAVITY_OAUTH_CLIENT_SECRET=
# WINDSURF_FIREBASE_API_KEY=
# ── Qwen (Alibaba) ──
QWEN_OAUTH_CLIENT_ID=f0304373b74a44d2b584a3fb70ca9e56
# ── Kimi Coding (Moonshot) ──
KIMI_CODING_OAUTH_CLIENT_ID=17e5f671-d194-4dfb-9706-5516cb48c098
@@ -1038,7 +1035,6 @@ KIRO_USER_AGENT="AWS-SDK-JS/3.0.0 kiro-ide/1.0.0"
# Used by: open-sse/executors/kiro.ts
# KIRO_VERIFY_FULL_CRC=false
QODER_USER_AGENT="Qoder-Cli"
QWEN_USER_AGENT="QwenCode/0.19.3 (linux; x64)"
CURSOR_USER_AGENT="Cursor/3.4"
# Override Codex client version sent in headers independently of the
@@ -1068,8 +1064,6 @@ CURSOR_USER_AGENT="Cursor/3.4"
# CLI_COMPAT_KIMI_CODING=1
# CLI_COMPAT_KILOCODE=1
# CLI_COMPAT_CLINE=1
# CLI_COMPAT_QWEN=1
# Or enable for all providers at once:
# CLI_COMPAT_ALL=1

View File

@@ -267,8 +267,8 @@ Zod schemas, and unit tests aligned when editing.
### Provider Categories
- **Free** (3): Qoder AI, Qwen Code, Kiro AI
- **OAuth** (14): Claude Code, Antigravity, Codex, GitHub Copilot, Cursor, Kimi Coding, Kilo Code, Cline, Qwen (⚠️ free tier discontinued 2026-04-15), Kiro, Qoder, Gemini, Windsurf (v3.8), GitLab Duo (v3.8)
- **Free** (2): Qoder AI, Kiro AI
- **OAuth** (13): Claude Code, Antigravity, Codex, GitHub Copilot, Cursor, Kimi Coding, Kilo Code, Cline, Kiro, Qoder, Gemini, Windsurf (v3.8), GitLab Duo (v3.8)
- **API Key** (120+): OpenAI, Anthropic, Gemini, DeepSeek, Groq, xAI, Mistral, Perplexity,
Together, Fireworks, Cerebras, Cohere, NVIDIA, Nebius, SiliconFlow, Hyperbolic,
HuggingFace, OpenRouter, Vertex AI, Cloudflare AI, Scaleway, AI/ML API, Pollinations,

View File

@@ -292,7 +292,6 @@ All **18** strategies — mix & match per combo step:
<td align="center" width="76"><a href="https://github.com/Kilo-Org/kilocode"><img src="./public/providers/kilocode.svg" width="40" alt="Kilo Code"/><br/><sub><b>Kilo Code</b></sub><br/><sub>                           </sub></a></td>
<td align="center" width="76"><img src="https://cdn.jsdelivr.net/npm/@lobehub/icons-static-png@1.91.0/dark/roocode.png#gh-dark-mode-only" width="40" alt="Roo Code"/><img src="https://cdn.jsdelivr.net/npm/@lobehub/icons-static-svg@1.91.0/icons/roocode.svg#gh-light-mode-only" width="40" alt="Roo Code"/><br/><sub><b>Roo Code</b></sub><br/><sub>                           </sub></td>
<td align="center" width="76"><img src="./public/providers/continue.svg" width="40" alt="Continue"/><br/><sub><b>Continue</b></sub><br/><sub>                           </sub></td>
<td align="center" width="76"><img src="./public/providers/qwen.svg" width="40" alt="Qwen Code"/><br/><sub><b>Qwen Code</b></sub><br/><sub>                           </sub></td>
<td align="center" width="76"><img src="./public/providers/cli-generic.svg" width="40" alt="Aider"/><br/><sub><b>Aider</b></sub><br/><sub>                           </sub></td>
<td align="center" width="76"><img src="./public/providers/cli-generic.svg" width="40" alt="ForgeCode"/><br/><sub><b>ForgeCode</b></sub><br/><sub>                           </sub></td>
</tr>

View File

@@ -42,7 +42,7 @@ Request → CORS → Authz pipeline (classify → policies → enforce)
| --------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
| **Dashboard Login** | Password-based auth with JWT tokens (HttpOnly cookies) |
| **API Key Auth** | HMAC-signed keys with CRC validation |
| **OAuth 2.0 + PKCE** | 14 providers (Claude, Codex, GitHub, Cursor, Antigravity, Gemini, Kimi Coding, Kilo Code, Cline, Qwen, Kiro, Qoder, Windsurf, GitLab Duo) |
| **OAuth 2.0 + PKCE** | 13 providers (Claude, Codex, GitHub, Cursor, Antigravity, Gemini, Kimi Coding, Kilo Code, Cline, Kiro, Qoder, Windsurf, GitLab Duo) |
| **Token Refresh** | Automatic OAuth token refresh before expiry |
| **Secure Cookies** | `AUTH_COOKIE_SECURE=true` for HTTPS environments |
| **Authz Pipeline** | Route classification (PUBLIC / CLIENT_API / MANAGEMENT) — see `docs/architecture/AUTHZ_GUIDE.md` |

View File

@@ -7,7 +7,6 @@ const PROVIDERS_WITH_OAUTH = [
{ id: "gemini", name: "Google Gemini", flow: "browser" },
{ id: "antigravity", name: "Antigravity", flow: "browser" },
{ id: "windsurf", name: "Windsurf", flow: "browser" },
{ id: "qwen", name: "Qwen Code", flow: "browser" },
{ id: "cursor", name: "Cursor", flow: "import" },
{ id: "zed", name: "Zed", flow: "import" },
{ id: "kiro", name: "Amazon Kiro", flow: "social" },

View File

@@ -70,8 +70,8 @@ import { registerSetupCursor } from "./setup-cursor.mjs";
import { registerSetupRoo } from "./setup-roo.mjs";
import { registerSetupCrush } from "./setup-crush.mjs";
import { registerSetupGoose } from "./setup-goose.mjs";
import { registerSetupQwen } from "./setup-qwen.mjs";
import { registerSetupAider } from "./setup-aider.mjs";
import { registerSetupQwen } from "./setup-qwen.mjs";
import { registerConnect } from "./connect.mjs";
import { registerContexts } from "./contexts.mjs";
import { registerTokens } from "./tokens.mjs";
@@ -153,8 +153,8 @@ export function registerCommands(program) {
registerSetupRoo(program);
registerSetupCrush(program);
registerSetupGoose(program);
registerSetupQwen(program);
registerSetupAider(program);
registerSetupQwen(program);
registerConnect(program);
registerContexts(program);
registerTokens(program);

View File

@@ -1,156 +1,166 @@
/**
* omniroute setup-qwen — configure Qwen Code (QwenLM/qwen-code) for OmniRoute.
*
* Qwen Code is a terminal AI agent with a file-based config at
* ~/.qwen/settings.json. For a custom OpenAI-compatible endpoint it uses a
* `modelProviders` entry with authType "openai", baseUrl WITH /v1, and an
* `envKey` naming the env var holding the key (secret stays in the env, never the
* file). Remote-aware; headless test via `qwen -p "..."`.
*/
/** Configure Qwen Code's OpenAI-compatible provider for OmniRoute. */
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
import { join } from "node:path";
import {
chmodSync,
existsSync,
mkdirSync,
readFileSync,
renameSync,
unlinkSync,
writeFileSync,
} from "node:fs";
import os from "node:os";
import { printHeading, printInfo, printSuccess, printError, createPrompt } from "../io.mjs";
import path from "node:path";
import {
mergeQwenCodeEnv,
mergeQwenCodeSettings,
normalizeQwenCodeBaseUrl,
} from "../../../src/shared/services/qwenCodeConfig.ts";
import { resolveActiveContext } from "../contexts.mjs";
import { createPrompt, printError, printHeading, printInfo, printSuccess } from "../io.mjs";
function ensureV1(url) {
const s = String(url || "").replace(/\/+$/, "");
return s.endsWith("/v1") ? s : `${s}/v1`;
}
/** Resolve baseUrl (WITH /v1) + apiKey from flags → active context → localhost. */
/** Resolve base URL and key from flags, active context, then local defaults. */
export function resolveQwenTarget(opts = {}) {
let root;
if (opts.remote) root = String(opts.remote).replace(/\/+$/, "");
else {
let root = opts.remote ? String(opts.remote) : "";
let context;
if (!root || !(opts.apiKey ?? opts["api-key"])) {
try {
root = resolveActiveContext(opts.context ?? process.env.OMNIROUTE_CONTEXT)?.baseUrl;
context = resolveActiveContext(opts.context ?? process.env.OMNIROUTE_CONTEXT);
} catch {
/* none */
}
if (!root) root = `http://localhost:${Number(opts.port ?? process.env.PORT ?? 20128) || 20128}`;
}
let apiKey = opts.apiKey ?? opts["api-key"];
if (!apiKey) {
try {
const c = resolveActiveContext(opts.context ?? process.env.OMNIROUTE_CONTEXT);
apiKey = c?.accessToken || c?.apiKey;
} catch {
/* none */
// An active context is optional for local setup.
}
}
if (!apiKey) apiKey = process.env.OMNIROUTE_API_KEY || "";
return { baseUrl: ensureV1(root), apiKey };
}
/** Merge the OmniRoute modelProvider into Qwen's settings.json (preserve rest). */
export function buildQwenSettings(existing, { baseUrl, model }) {
const s = existing && typeof existing === "object" ? { ...existing } : {};
const providers = Array.isArray(s.modelProviders)
? s.modelProviders.filter((p) => p?.id !== "omniroute")
: [];
providers.push({
id: "omniroute",
name: "OmniRoute",
authType: "openai",
baseUrl,
envKey: "OMNIROUTE_API_KEY",
});
s.modelProviders = providers;
if (model) {
s.selectedProvider = "omniroute";
s.model = model;
if (!root) root = context?.baseUrl || "";
if (!root) {
const port = Number(opts.port ?? process.env.PORT ?? 20128) || 20128;
root = `http://localhost:${port}`;
}
return s;
const apiKey =
opts.apiKey ??
opts["api-key"] ??
context?.accessToken ??
context?.apiKey ??
process.env.OMNIROUTE_API_KEY ??
"sk_omniroute";
return { baseUrl: normalizeQwenCodeBaseUrl(root), apiKey };
}
function readJson(path) {
try {
if (existsSync(path)) return JSON.parse(readFileSync(path, "utf8"));
} catch {
/* corrupt/missing */
const readSettings = (filePath) => {
if (!existsSync(filePath)) return {};
const parsed = JSON.parse(readFileSync(filePath, "utf8"));
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
throw new Error("Qwen Code settings.json must contain a JSON object");
}
return {};
}
return parsed;
};
async function fetchModelIds(baseUrl, apiKey) {
const readText = (filePath) => (existsSync(filePath) ? readFileSync(filePath, "utf8") : "");
const writeAtomic = (filePath, content, mode) => {
const tempPath = `${filePath}.${process.pid}.${Date.now()}.tmp`;
try {
const headers = { "Content-Type": "application/json" };
if (apiKey) headers["Authorization"] = `Bearer ${apiKey}`;
const res = await fetch(`${baseUrl.replace(/\/v1$/, "")}/v1/models`, {
headers,
writeFileSync(tempPath, content, { encoding: "utf8", mode });
if (mode !== undefined) chmodSync(tempPath, mode);
renameSync(tempPath, filePath);
} catch (error) {
try {
unlinkSync(tempPath);
} catch {
// The temporary file may not have been created.
}
throw error;
}
};
const fetchModelIds = async (baseUrl, apiKey) => {
try {
const response = await fetch(`${baseUrl}/models`, {
headers: { Authorization: `Bearer ${apiKey}` },
signal: AbortSignal.timeout(8000),
});
if (!res.ok) return [];
const body = await res.json();
const list = Array.isArray(body) ? body : (body.data ?? body.models ?? []);
return list.map((m) => (typeof m === "string" ? m : m?.id)).filter(Boolean);
if (!response.ok) return [];
const body = await response.json();
const models = Array.isArray(body) ? body : (body.data ?? body.models ?? []);
return models.map((entry) => (typeof entry === "string" ? entry : entry?.id)).filter(Boolean);
} catch {
return [];
}
}
};
export async function runSetupQwenCommand(opts = {}) {
const { baseUrl, apiKey } = resolveQwenTarget(opts);
const dryRun = Boolean(opts.dryRun ?? opts["dry-run"]);
const configPath =
opts.configPath ?? opts["config-path"] ?? join(os.homedir(), ".qwen", "settings.json");
const settingsPath =
opts.configPath ?? opts["config-path"] ?? path.join(os.homedir(), ".qwen", "settings.json");
const envPath = opts.envPath ?? opts["env-path"] ?? path.join(path.dirname(settingsPath), ".env");
printHeading("OmniRoute → Qwen Code (openai-compatible)");
printHeading("OmniRoute → Qwen Code (OpenAI-compatible)");
printInfo(`baseUrl: ${baseUrl}`);
let model = opts.model;
if (!model) {
const ids = await fetchModelIds(baseUrl, apiKey);
if (ids.length && !opts.yes) {
printInfo(`Examples: ${ids.slice(0, 20).join(", ")}${ids.length > 20 ? " …" : ""}`);
const prompt = createPrompt();
try {
model = await prompt.ask("Model id for Qwen");
} finally {
prompt.close();
}
let model = String(opts.model || "").trim();
if (!model && !opts.yes) {
const modelIds = await fetchModelIds(baseUrl, apiKey);
if (modelIds.length > 0) {
printInfo(`Examples: ${modelIds.slice(0, 20).join(", ")}${modelIds.length > 20 ? " …" : ""}`);
}
const prompt = createPrompt();
try {
model = String(await prompt.ask("Model id for Qwen Code")).trim();
} finally {
prompt.close();
}
}
if (!model) {
printError("A model is required. Pass --model <id>.");
return 2;
}
const merged = buildQwenSettings(readJson(configPath), { baseUrl, model });
const out = JSON.stringify(merged, null, 2) + "\n";
try {
const settings = mergeQwenCodeSettings(readSettings(settingsPath), { baseUrl, model });
const envText = mergeQwenCodeEnv(readText(envPath), apiKey);
const settingsText = `${JSON.stringify(settings, null, 2)}\n`;
if (dryRun) {
console.log("\n" + out);
printInfo(`[dry-run] ${configPath}`);
} else {
mkdirSync(join(configPath, ".."), { recursive: true });
writeFileSync(configPath, out, "utf8");
printSuccess(`Wrote ${configPath}`);
if (dryRun) {
console.log(`\n${settingsText}`);
printInfo(`[dry-run] settings → ${settingsPath}`);
printInfo(`[dry-run] credential → ${envPath} (OMNIROUTE_API_KEY)`);
return 0;
}
mkdirSync(path.dirname(settingsPath), { recursive: true, mode: 0o700 });
mkdirSync(path.dirname(envPath), { recursive: true, mode: 0o700 });
writeAtomic(settingsPath, settingsText);
writeAtomic(envPath, envText, 0o600);
printSuccess(`Wrote ${settingsPath}`);
printSuccess(`Updated ${envPath} (OMNIROUTE_API_KEY only)`);
printInfo('Run: qwen (or headless: qwen -p "reply OK")');
return 0;
} catch (error) {
printError(`Failed to configure Qwen Code: ${error?.message || error}`);
return 1;
}
printInfo(
"\nProvide the key (settings reference OMNIROUTE_API_KEY): export OMNIROUTE_API_KEY=..."
);
printInfo('Then run: qwen (or headless: qwen -p "reply OK")');
return 0;
}
export function registerSetupQwen(program) {
program
.command("setup-qwen")
.description(
"Configure Qwen Code for OmniRoute: write ~/.qwen/settings.json (openai modelProvider)"
)
.description("Configure Qwen Code's upstream V4 modelProviders format for OmniRoute")
.option("--port <port>", "Local OmniRoute port (ignored when --remote is set)", "20128")
.option("--remote <url>", "Remote OmniRoute URL, e.g. http://192.168.0.15:20128")
.option("--api-key <key>", "OmniRoute API key (defaults to OMNIROUTE_API_KEY env var)")
.option("--model <id>", "Model id for Qwen (required unless picked interactively)")
.option("--config-path <path>", "settings.json path (default: ~/.qwen/settings.json)")
.option("--yes", "Non-interactive: do not prompt (requires --model)")
.option("--dry-run", "Print what would be written without touching the filesystem")
.option("--remote <url>", "Remote OmniRoute URL")
.option("--api-key <key>", "OmniRoute API key")
.option("--model <id>", "Model id for Qwen Code")
.option("--config-path <path>", "Qwen Code settings.json path")
.option("--env-path <path>", "Qwen Code .env path")
.option("--yes", "Non-interactive; requires --model")
.option("--dry-run", "Print settings without writing files or secrets")
.action(async (opts) => {
const code = await runSetupQwenCommand(opts);
if (code !== 0) process.exit(code);
if (code !== 0) process.exitCode = code;
});
}

View File

@@ -1 +1 @@
- Realigned 13 test files that had drifted red on the release tip after the 102-PR merge campaign (full-suite sweep): provider counts/goldens/docs regenerated, qwen OAuth + 1M-beta assertions caught up with the live-validated behavior, and two real fixes — legacy `refresh_token` column healed before its index is created, and `shouldSkipCloudSyncInitialization` no longer swaps its `(env, argv)` arguments.
- Realigned 13 test files that had drifted red on the release tip after the 102-PR merge campaign (full-suite sweep): provider counts/goldens/docs regenerated, 1M-beta assertions caught up with the live-validated behavior, and two real fixes — legacy `refresh_token` column healed before its index is created, and `shouldSkipCloudSyncInitialization` no longer swaps its `(env, argv)` arguments.

View File

@@ -1 +1 @@
- **chore(quality):** split `open-sse/services/tokenRefresh.ts` (2249 lines, frozen) into per-provider leaves — `open-sse/services/tokenRefresh/providers/{windsurf,codebuddyCn,cline,kimiCoding,gitlabDuo,claudeOAuth,google,qwen,codex,kiro,qoder,github,copilot}.ts` plus a `tokenRefresh/shared.ts` for the cross-provider OAuth-error classifier and form-body helper — pure move, zero behavior change; the file shrinks to ~989 lines and every previously-public export (`refreshCopilotToken`, `getAccessToken`, `extractOAuthErrorCode`, etc.) is re-exported unchanged so no importer needed to change. Idea originally proposed by @KooshaPari in #7338 against a much older base; redone here against the current tip.
- **chore(quality):** split `open-sse/services/tokenRefresh.ts` (2249 lines, frozen) into per-provider leaves — `open-sse/services/tokenRefresh/providers/{windsurf,codebuddyCn,cline,kimiCoding,gitlabDuo,claudeOAuth,google,codex,kiro,qoder,github,copilot}.ts` plus a `tokenRefresh/shared.ts` for the cross-provider OAuth-error classifier and form-body helper — pure move, zero behavior change; the file shrinks to ~989 lines and every previously-public export (`refreshCopilotToken`, `getAccessToken`, `extractOAuthErrorCode`, etc.) is re-exported unchanged so no importer needed to change. Idea originally proposed by @KooshaPari in #7338 against a much older base; redone here against the current tip.

View File

@@ -202,11 +202,6 @@
"count": 1
}
},
"src/app/(dashboard)/dashboard/cli-code/components/DefaultToolCard.tsx": {
"react-hooks/exhaustive-deps": {
"count": 2
}
},
"src/app/(dashboard)/dashboard/costs/costExplorerUtils.ts": {
"no-restricted-syntax": {
"count": 1
@@ -709,7 +704,7 @@
},
"tests/unit/chatcore-translation-paths.test.ts": {
"@typescript-eslint/no-explicit-any": {
"count": 40
"count": 34
}
},
"tests/unit/chatgpt-web-tools-5240.test.ts": {
@@ -1354,7 +1349,7 @@
},
"tests/unit/executor-default-base.test.ts": {
"@typescript-eslint/no-explicit-any": {
"count": 43
"count": 42
}
},
"tests/unit/executor-github.test.ts": {
@@ -1457,11 +1452,6 @@
"count": 25
}
},
"tests/unit/guide-settings-route.test.ts": {
"@typescript-eslint/no-explicit-any": {
"count": 4
}
},
"tests/unit/i18n-nest-dotted-keys.test.ts": {
"@typescript-eslint/no-explicit-any": {
"count": 4
@@ -1749,7 +1739,7 @@
},
"tests/unit/provider-models-route.test.ts": {
"@typescript-eslint/no-explicit-any": {
"count": 55
"count": 54
}
},
"tests/unit/provider-models-token-limits.test.ts": {
@@ -2234,7 +2224,7 @@
},
"tests/unit/usage-service-hardening.test.ts": {
"@typescript-eslint/no-explicit-any": {
"count": 59
"count": 56
}
},
"tests/unit/v1-ws-bridge.test.ts": {
@@ -2297,4 +2287,4 @@
"count": 5
}
}
}
}

View File

@@ -91,7 +91,7 @@
"_rebaseline_2026_06_18_qg9_chatcore_split_prB": "QG v2 Fase 9 T5 C6-C7: chatCore.ts 5265->5060 (shrink, stacked on prA #4188) — two pure, byte-identical extractions into open-sse/handlers/chatCore/. (1) comboContextCache.ts (<cap): the combo/upstream-proxy context caches — getCombosCached/clearCombosCache/getUpstreamProxyConfigCached/clearUpstreamProxyConfigCache plus their consts COMBOS_CACHE_TTL/PROXY_CONFIG_CACHE_TTL AND the two module-level cache Maps (_proxyConfigCache + the _combosPromise/_combosCacheTs/_combosCacheVersionSnapshot combos cache) moved WITH their accessor functions so each Map stays a single instance in one module (state cohesion — no double-state). clearCombosCache/clearUpstreamProxyConfigCache re-exported from chatCore.ts (combo-cache-invalidation + chatcore-translation-paths tests). (2) executorHelpers.ts (<cap): resolveAccountSemaphoreKey/resolveAccountSemaphoreAccountKey/resolveAccountSemaphoreMaxConcurrency + buildClaudePromptCacheLogMeta; the private toFiniteNumberOrNull helper (its only remaining caller was resolveAccountSemaphoreMaxConcurrency) moved with them rather than creating a barrel import. Orphaned imports dropped from chatCore.ts (buildAccountSemaphoreKey, getUpstreamProxyConfig). No re-export needed for the executor helpers (none were ever public). Pure move, no logic change.",
"_rebaseline_2026_06_18_qg9_chatcore_split_prA": "QG v2 Fase 9 T5 C2-C3-C5: chatCore.ts 5445->5265 (wc -l 5264 + 1). Fase 2 of the chatCore split following #4159 (which created 10 leaf modules). Three more sibling leaves created under open-sse/handlers/chatCore/, all <cap, pure byte-identical moves (no runtime change): passthroughHelpers.ts (C2: shouldUseNativeCodexPassthrough/redactPassthroughThinkingSignatures/isClaudeCodeSemanticPassthroughRequest), responseHeaders.ts (C3: STREAMING_RESPONSE_HEADER_DENYLIST/buildStreamingResponseHeaders/materializeDeduplicatedExecutionResult/stripStaleForwardingHeaders), telemetryHelpers.ts (C5: forwardDashboardEventToLiveWs/maybeSyncClaudeExtraUsageState). chatCore.ts imports all 8 still-referenced symbols back from the leaves and re-exports the 5 previously-public ones (shouldUseNativeCodexPassthrough/redactPassthroughThinkingSignatures/isClaudeCodeSemanticPassthroughRequest/buildStreamingResponseHeaders/stripStaleForwardingHeaders) so existing test importers keep resolving. No barrel imports in the new leaves.",
"_rebaseline_2026_06_18_4176_free_models": "PR #4176 own growth: AddApiKeyModal.tsx 845->866 (+21) and EditConnectionModal.tsx 1174->1204 (+30) = the 'import only free models' connection option — a free-models Toggle gated by providerHasFreeModels() plus its form-state wiring (importFreeModelsOnly field + providerSpecificData persistence, explicit-false on edit so the PUT merge doesn't keep a stale true) added to both connection modals, mirroring the prior per-modal toggle bumps #3879 (redact-thinking) and #2997 (disable-cooling). Detection lives in the new shared src/shared/utils/freeModels.ts (112 LOC, <cap); the duplicated Toggle is ~6 lines and the per-modal state wiring is intrinsic, so the remaining growth is cohesive UI, not an extractable block.",
"_rebaseline_2026_06_18_3931_qwen_web_models": "Issue #3931 (Problem #3) own growth: providers/[id]/models/route.ts 2512->2531 (+19 = one PROVIDER_MODELS_CONFIG entry for `qwen-web` + a 4-line comment). qwen-web was missing from the config map so its model-discovery page returned nothing (the OAuth fallback only fires for provider===qwen). Pure additive config entry pointing at the public chat.qwen.ai/api/v2/models endpoint; standard per-provider addition, not extractable.",
"_rebaseline_2026_06_18_3931_qwen_web_models": "Issue #3931 (Problem #3) own growth: providers/[id]/models/route.ts 2512->2531 (+19 = one PROVIDER_MODELS_CONFIG entry for `qwen-web` + a 4-line comment). qwen-web was missing from the config map so its model-discovery page returned nothing. Pure additive config entry pointing at the public chat.qwen.ai/api/v2/models endpoint; standard per-provider addition, not extractable.",
"_rebaseline_2026_06_18_4165_queue_timeout_msg": "Issue #4165 own growth: rateLimitManager.ts 1022->1035 (+13 at the existing withRateLimit catch chokepoint). Bottleneck's raw `This job timed out after <maxWaitMs> ms.` is rewritten into a clear OmniRoute-owned error (names resilienceSettings.requestQueue.maxWaitMs, disclaims upstream, keeps the original as `cause`, tags code=RATE_LIMIT_QUEUE_TIMEOUT) so queue-saturation 502s stop masquerading as provider outages. The branch already existed (it only logged); this adds the error construction at the same point. Not extractable — closes over provider/model/maxWaitMs locals of the single catch.",
"_rebaseline_2026_06_18_qg9_combo_split_d4": "QG v2 Fase 9 T5 D4: combo.ts 4740->4589 — shadow routing extracted byte-identically to the new open-sse/services/combo/shadowRouting.ts (<cap, normalizeShadowRoutingConfig/resolveShadowTargets/drainShadowResponse/withTimeout/cloneRequestBodyForShadowRouting/scheduleShadowRouting). The shared isRecord guard moved to the new combo/comboData.ts leaf (<cap) so submodules can use it without importing the barrel. resolveShadowTargets/scheduleShadowRouting re-exported from combo.ts for compatibility (handleComboChat/handleRoundRobinCombo callers unchanged). Pure move, no logic change.",
"_rebaseline_2026_06_18_qg9_combo_split_d5": "QG v2 Fase 9 T5 D5: combo.ts 4589->4430 — target sorters extracted byte-identically to the new open-sse/services/combo/targetSorters.ts (<cap, normalizeModelEntry/selectWeightedTarget/orderTargetsForWeightedFallback/sortModelsByCost/sortTargetsByCost/sortModelsByUsage/sortTargetsByUsage/getP2CTargetScore/orderTargetsByPowerOfTwoChoices). None were ever public, so combo.ts imports back only the six still called by code that stays (no re-export). getComboStepTarget/getComboStepWeight imports dropped from combo.ts (only normalizeModelEntry used them). Pure move, no logic change.",
@@ -306,7 +306,6 @@
"_rebaseline_2026_06_27_5193_antigravity_test": "#5193 own test growth: oauth-providers-config.test.ts 870->873 (+3: antigravity projectId assertion + 50ms tick for the now fire-and-forget onboarding, matching the no-PKCE/no-openid flow).",
"_rebaseline_2026_07_02_5928_base_red": "web-cookie-providers-new.test.ts 845->850: #5928 (test(security) Kimi Web URL host parse, CodeQL #689) grew the file +5 lines and merged into release/v3.8.44 WITHOUT rebaselining, leaving a fast-gates base-red that blocked every subsequent PR->release. Test growth is legitimate (a security regression test); maintainer absorbs the drift here. Frozen at 850.",
"_rebaseline_basered_codebuddy_cn": "Base-red fix (#4664 CodeBuddy CN): oauth-providers-config.test.ts 867->870 (+3) to align the EXPECTED provider list/config with the codebuddy-cn provider that #4664 added to the registry without updating this test (it asserts 'exactly once').",
"_rebaseline_pr4561_qwen_oauth_url": "Reconcile #4561 (port decolua/9router#683) already-merged growth: oauth-providers-config.test.ts 855->867 (+12, qwen.ai URL regression-pin test). Fast-gate PR->release does not run check:file-size, so this surfaced post-merge.",
"_rebaseline_pr4613_compatible_provider_groups": "Reconcile #4613 already-merged growth: providers-page-utils.test.ts 1004->1052 (+48, buildCompatibleProviderGroups partition unit test). Fast-gate PR->release does not run check:file-size, so this surfaced post-merge.",
"tests/integration/chat-pipeline.test.ts": 1601,
"tests/integration/chatcore-compression-integration.test.ts": 1111,
@@ -423,4 +422,4 @@
"_rebaseline_2026_07_07_6525_chirag_image_guard": "PR #6525 (@chirag127, #6457) own growth: chat.ts ->1778 (reject image-only models on /v1/chat/completions; stacks on #6515). Owner-approved. Frozen.",
"_rebaseline_2026_07_15_7045_perf_instrumentation": "PR #7045 (@oyi77) own growth: open-sse/utils/stream.ts 2796->2814 (+18) from performance.mark/measure instrumentation around the SSE dispatch chokepoint (b48ba21c4), a TextEncoder hoisting fix to avoid a per-chunk allocation on the hot path (c35e8a9b4), and clearing the fixed-name \"omni-request-body-size\" mark immediately after creation (babysit fix, addressing a review-flagged unbounded-growth leak in Node's global performance timeline). Cohesive wiring at the existing stream-dispatch chokepoint; not extractable. Covered by tests/unit/chatcore-streaming-pipeline.test.ts + tests/unit/stream-request-body-size-mark-7045.test.ts.",
"_rebaseline_2026_07_18_basereds_test_realignment": "Base-red sweep own growth (post 102-PR campaign, full-suite realignment): tests/unit/combo-routing-engine.test.ts 3209->3243 (+34 = least-used tests now prime usage through real handleComboChat calls so recordComboRequest keys by the resolved executionKey exactly as production does — #7015 keying); tests/unit/db-migration-runner.test.ts 1491->1499 (+8 = withNonTestEnvironment now also strips node --test tokens from process.execArgv, matching the #7359 isAutomatedTestProcess widening); tests/unit/executor-default-base.test.ts 1523->1527 (+4 = 1M-beta assertion updated for claude-sonnet-4-6 GA #7129). All three are test-fidelity realignments, not extractable."
}
}

View File

@@ -175,7 +175,7 @@ flowchart LR
end
subgraph Upstreams[Upstream Providers]
P1[OAuth Providers\nClaude/Codex/Gemini/Qwen/Qoder/GitHub/Kiro/Cursor/Antigravity]
P1[OAuth Providers\nClaude/Codex/Gemini/Qoder/GitHub/Kiro/Cursor/Antigravity]
P2[API Key Providers\nOpenAI/Anthropic/OpenRouter/GLM/Kimi/MiniMax\nDeepSeek/Groq/xAI/Mistral/Perplexity\nTogether/Fireworks/Cerebras/Cohere/NVIDIA]
P3[Compatible Nodes\nOpenAI-compatible / Anthropic-compatible]
end
@@ -948,7 +948,6 @@ All other providers (including custom compatible nodes) use the `DefaultExecutor
| GitHub Copilot | openai | OAuth + Copilot Token | ✅ | ✅ | ✅ | ✅ Quota snapshots |
| Cursor | cursor | Custom checksum | ✅ | ✅ | ❌ | ❌ |
| Kiro | kiro | AWS SSO OIDC | ✅ (EventStream) | ❌ | ✅ | ✅ Usage limits |
| Qwen | openai | OAuth | ✅ | ✅ | ✅ | ⚠️ Per request |
| Qoder | openai | OAuth / PAT | ✅ | ✅ | ✅ | ⚠️ Per request |
| Kilo Code | openai | OAuth | ✅ | ✅ | ✅ | ❌ |
| Cline | openai | OAuth | ✅ | ✅ | ✅ | ❌ |

View File

@@ -293,7 +293,7 @@ table groups the actual directories and notable top-level files.
| `jobs/` | Background jobs (`autoUpdate.ts`, …) |
| `memory/` | Persistent memory: `store.ts`, `cache.ts`, `retrieval.ts`, `summarization.ts`, `extraction.ts`, `injection.ts`, `qdrant.ts`, `settings.ts`, `verify.ts`, `schemas.ts`, `types.ts` |
| `monitoring/` | `observability.ts` |
| `oauth/` | OAuth providers (14): `antigravity`, `claude`, `cline`, `codex`, `cursor`, `gemini`, `github`, `gitlab-duo`, `kilocode`, `kimi-coding`, `kiro`, `qoder`, `qwen`, `windsurf` plus `services/`, `utils/{pkce, server, banner, codexAuthFile, ui}`, `constants/oauth.ts` |
| `oauth/` | OAuth providers (13): `antigravity`, `claude`, `cline`, `codex`, `cursor`, `gemini`, `github`, `gitlab-duo`, `kilocode`, `kimi-coding`, `kiro`, `qoder`, `windsurf` plus `services/`, `utils/{pkce, server, banner, codexAuthFile, ui}`, `constants/oauth.ts` |
| `plugins/` | Plugin loader (`index.ts`) |
| `promptCache/` | `prefixAnalyzer.ts`, `index.ts` |
| `providerModels/` | Managed model lifecycle: `modelDiscovery.ts`, `managedModelImport.ts`, `managedAvailableModels.ts`, `cursorAgent.ts` |

View File

@@ -182,7 +182,7 @@ src/
| `compliance/` | Audit log + provider audit — see `docs/security/COMPLIANCE.md` |
| `compression/` | Compression engine glue (engines live in `open-sse/services/compression/`) |
| `config/` | Runtime config helpers |
| `db/` | 95+ domain DB modules + 110+ migrations (always go through here for SQLite) |
| `db/` | 95+ domain DB modules + 110+ migrations (always go through here for SQLite) |
| `quota/` | Quota Sharing Engine: `dimensions.ts` (types/Zod), `types.ts` (QuotaStore interface), `sqliteQuotaStore.ts`, `redisQuotaStore.ts`, `storeFactory.ts`, `fairShare.ts`, `burnRate.ts`, `planResolver.ts`, `planRegistry.ts`, `saturationSignals.ts`, `enforce.ts`, `spendRecorder.ts` — see `docs/routing/QUOTA_SHARE.md` |
| `display/` | UI formatting helpers (cost, latency, etc.) |
| `embeddings/` | Embeddings service helpers |
@@ -195,7 +195,7 @@ src/
| `memory/vectorStore.ts` | sqlite-vec v0.1.9 wrapper — KNN brute-force + hybrid RRF (FTS5 + vector, k=60). Lazy-init, degrades gracefully when sqlite-vec unavailable. (plan 21) |
| `memory/reindex.ts` | `runReindexBatch()` — processes memories with `needs_reindex=1` in background; called by `POST /api/memory/reindex` and lazy-backfill path. (plan 21) |
| `monitoring/` | Health checks, metrics emission |
| `oauth/` | OAuth flows for 14 providers (claude, codex, antigravity, cursor, github, gemini, kimi-coding, kilocode, cline, qwen, kiro, qoder, gitlab-duo, windsurf) |
| `oauth/` | OAuth flows for 13 providers (claude, codex, antigravity, cursor, github, gemini, kimi-coding, kilocode, cline, kiro, qoder, gitlab-duo, windsurf) |
| `plugins/` | Plugin registry |
| `promptCache/` | Anthropic-style prompt cache breakpoints |
| `skills/` | Skills framework (built-in + marketplace + SkillsSH) — see `docs/frameworks/SKILLS.md` |

View File

@@ -184,7 +184,7 @@ Combo: "free-forever"
Pipeline: RTK -> Caveman
Targets:
1. if/kimi-k2-thinking
2. qw/qwen3-coder-plus
2. if/qwen3-coder-plus
```
This lets you use stacked compression on free/coding providers while keeping lite mode on paid

View File

@@ -19,7 +19,7 @@ ACP (Agent Client Protocol) is a **"CLI-as-backend" transport** for OmniRoute. I
| **No API keys needed** | Uses your existing CLI authentication |
| **Native protocol** | Uses each CLI's native input/output format |
| **Auto-discovery** | Detects installed CLIs on your system |
| **14 built-in agents** | Pre-configured for popular CLI tools |
| **13 built-in agents** | Pre-configured for popular CLI tools |
| **Custom agents** | Add your own CLI tools via settings |
| **Process management** | Handles lifecycle (spawn, send, kill) |
@@ -27,7 +27,7 @@ ACP (Agent Client Protocol) is a **"CLI-as-backend" transport** for OmniRoute. I
## Supported CLI Agents
ACP supports **14 built-in CLI agents** out of the box:
ACP supports **13 built-in CLI agents** out of the box:
| Agent ID | Display Name | Binary | Protocol |
| ------------- | ------------------ | ------------- | -------- |
@@ -38,7 +38,7 @@ ACP supports **14 built-in CLI agents** out of the box:
| `aider` | Aider | `aider` | stdio |
| `opencode` | OpenCode | `opencode` | stdio |
| `cline` | Cline | `cline` | stdio |
| `qwen-code` | Qwen Code | `qwen` | stdio |
| `qwen` | Qwen Code | `qwen --acp` | stdio |
| `forge` | ForgeCode | `forge` | stdio |
| `amazon-q` | Amazon Q Developer | `q` | stdio |
| `interpreter` | Open Interpreter | `interpreter` | stdio |
@@ -407,7 +407,7 @@ Each ACP session runs in its own child process. The process is killed when the s
**Problem**: `acpManager.spawn()` throws `Unknown agent: <id>`
**Solution**: Only 4 agents are allowed in `spawn()`:
**Solution**: Only these agents are allowed in `spawn()`:
- `claude`
- `codex`

View File

@@ -11,8 +11,8 @@ CLI (Codex, Claude Code, OpenCode, Cline, …) to use OmniRoute as its backend
the tool talks to **one** endpoint and OmniRoute routes to the right provider with
auto-fallback. Each command reads the **live** model catalog from a running
OmniRoute (local or remote) and writes the tool's own config file on **your**
machine. The API key is referenced by env var wherever the tool supports it, so the
secret is never written to disk (the exceptions are noted below).
machine. The API key is referenced by an environment variable wherever the tool
supports it. Commands that persist a tool-local environment file are noted below.
There are also two launchers — `omniroute launch` (Claude Code) and
`omniroute launch-codex` (Codex) — that spawn the CLI with the right env injected,
@@ -47,8 +47,8 @@ server and writes the config locally.
| `omniroute setup-roo` | Roo Code | `~/.omniroute/roo-settings.json` (import doc) + sets `roo-cline.autoImportSettingsPath` if a VS Code `settings.json` exists | `--remote` `--api-key` `--model` `--yes` `--dry-run` `--port` `--import-path` `--vscode-settings` | Both |
| `omniroute setup-crush` | Crush | `~/.config/crush/crush.json``openai-compat` provider, key via `$OMNIROUTE_API_KEY` | `--remote` `--api-key` `--only` `--dry-run` `--port` `--config-path` | Both |
| `omniroute setup-goose` | Goose | `~/.config/goose/config.yaml` (`GOOSE_PROVIDER`/`OPENAI_HOST`/`GOOSE_MODEL`) + prints env recipe | `--remote` `--api-key` `--model` `--yes` `--dry-run` `--port` `--config-path` | Both |
| `omniroute setup-qwen` | Qwen Code | `~/.qwen/settings.json` — openai `modelProvider`, key via `envKey` (`OMNIROUTE_API_KEY`) | `--remote` `--api-key` `--model` `--yes` `--dry-run` `--port` `--config-path` | Both |
| `omniroute setup-aider` | Aider | `~/.aider.conf.yml` (`openai-api-base` + `model: openai/<id>`) + prints env recipe | `--remote` `--api-key` `--model` `--yes` `--dry-run` `--port` `--config-path` | Both |
| `omniroute setup-qwen` | Qwen Code | `~/.qwen/settings.json` — V4 `modelProviders.openai` array + `OMNIROUTE_API_KEY` in `~/.qwen/.env` | `--remote` `--api-key` `--model` `--yes` `--dry-run` `--port` `--config-path` `--env-path` | Both |
| `omniroute launch` | Claude Code | Nothing — spawns `claude` with `ANTHROPIC_BASE_URL`/`ANTHROPIC_AUTH_TOKEN` injected | `--remote` `--api-key` `--token` `--profile` `--port` | Both |
| `omniroute launch-codex` | OpenAI Codex CLI | Nothing — spawns `codex` with the `omniroute` provider injected via `-c` flags | `--remote` `--api-key` `--profile` (`-p`) `--port` | Both |
@@ -101,7 +101,7 @@ opencode -m omniroute/glm/glm-5.2 "..."
# Tools without auto-discovery need an explicit model:
omniroute setup-aider --model glm/glm-5.2
omniroute setup-qwen --model kmc/kimi-k2.7
omniroute setup-qwen --model qwen/qwen3.8-max-preview
# Preview without writing anything:
omniroute setup-continue --dry-run
@@ -161,9 +161,10 @@ tool expects (verified in the command source):
| `setup-cline` (`openAiBaseUrl`) | root | No — Cline appends `/v1/chat/completions` |
| `setup-goose` (`OPENAI_HOST`) | root | No — Goose appends the path |
| `setup-aider` (`OPENAI_API_BASE`) | root | No — LiteLLM appends `/v1/chat/completions` |
| `setup-kilo`, `setup-roo`, `setup-continue`, `setup-crush`, `setup-qwen`, `setup-cursor` | with `/v1` | Yes |
| `setup-kilo`, `setup-roo`, `setup-continue`, `setup-crush`, `setup-cursor` | with `/v1` | Yes |
| `setup-claude` (`ANTHROPIC_BASE_URL`), `launch` | root | No — Claude Code appends `/v1/messages` |
| `setup-codex`, `launch-codex` (`model_providers.omniroute.base_url`) | with `/v1` | Yes |
| `setup-qwen` (`modelProviders.openai[].baseUrl`) | with `/v1` | Yes |
---

View File

@@ -51,7 +51,7 @@ The v3.7.x → v3.8.0 cycle added zero-config auto routing, new providers, OAuth
## 🔌 Providers
Manage AI provider connections: OAuth providers (Claude Code, Codex), API key providers (Groq, DeepSeek, OpenRouter), and free providers (Qoder, Qwen, Kiro). Kiro accounts include credit balance tracking — remaining credits, total allowance, and renewal date visible in Dashboard → Usage.
Manage AI provider connections: OAuth providers (Claude Code, Codex), API key providers (Groq, DeepSeek, OpenRouter), and free providers (Qoder, Kiro). Kiro accounts include credit balance tracking — remaining credits, total allowance, and renewal date visible in Dashboard → Usage.
OpenRouter connections can store a per-connection `preset` in Advanced Settings. When set, OmniRoute sends it as the OpenRouter top-level request field, for example `"preset": "email-copywriter"`, unless the client request already supplied its own `preset`.
@@ -136,7 +136,7 @@ One-click configuration for AI coding tools: Claude Code, Codex CLI, OpenClaw, K
## 🤖 CLI Agents _(v2.0.11+)_
Dashboard for discovering and managing CLI agents. Shows a grid of 17 built-in agents (Codex, Claude, Goose, OpenClaw, Aider, OpenCode, Cline, Qwen Code, ForgeCode, Amazon Q, Open Interpreter, Cursor CLI, Warp, **Windsurf**, **Devin CLI**, **Kimi Coding**, **Command Code**) with:
Dashboard for discovering and managing CLI agents. Shows a grid of 16 built-in agents (Codex, Claude, Goose, OpenClaw, Aider, OpenCode, Cline, ForgeCode, Amazon Q, Open Interpreter, Cursor CLI, Warp, **Windsurf**, **Devin CLI**, **Kimi Coding**, **Command Code**) with:
- **Installation status** — Installed / Not Found with version detection
- **Protocol badges** — stdio, HTTP, etc.

View File

@@ -215,8 +215,8 @@ context, or `--remote <url> --api-key <key>`):
| Roo Code | `omniroute setup-roo` | writes a Roo import JSON (`~/.omniroute/roo-settings.json`) + sets `roo-cline.autoImportSettingsPath` + prints UI steps (OpenAI-compatible, Base URL **with** `/v1`) |
| Crush | `omniroute setup-crush` | `~/.config/crush/crush.json``openai-compat` provider, `base_url` **with** `/v1`, key via `$OMNIROUTE_API_KEY` |
| Goose | `omniroute setup-goose` | `~/.config/goose/config.yaml` (`GOOSE_PROVIDER=openai` + `OPENAI_HOST` **without** `/v1` + `GOOSE_MODEL`) + env recipe |
| Qwen Code | `omniroute setup-qwen` | `~/.qwen/settings.json` — openai `modelProvider`, `baseUrl` **with** `/v1`, key via `envKey` (OMNIROUTE_API_KEY) |
| Aider | `omniroute setup-aider` | `~/.aider.conf.yml` (`openai-api-base` **without** `/v1` + `model: openai/<id>`) + env recipe (`aider --message --yes`) |
| Qwen Code | `omniroute setup-qwen` | `~/.qwen/settings.json` V4 `modelProviders.openai` entry + `OMNIROUTE_API_KEY` in `~/.qwen/.env` |
```bash
# OpenCode (openai-compatible provider, all catalog models, remote VPS)

View File

@@ -179,8 +179,8 @@ omniroute setup-cursor # prints Cursor's in-app steps
omniroute setup-roo # Roo Code import + autoImport pointer
omniroute setup-crush # ~/.config/crush/crush.json
omniroute setup-goose # ~/.config/goose/config.yaml
omniroute setup-qwen # ~/.qwen/settings.json
omniroute setup-aider # ~/.aider.conf.yml
omniroute setup-qwen # ~/.qwen/settings.json + ~/.qwen/.env
```
Each accepts `--remote <url> --api-key <key>` to configure a local tool against a

View File

@@ -83,7 +83,7 @@ vs. $20 + hitting limits = frustration
```
Combo: "free-forever"
1. if/kimi-k2 (unlimited free)
2. qw/qwen3-coder-plus (unlimited free)
2. kr/qwen3-coder-next (free fallback)
Monthly cost: $0
Quality: Production-ready models
@@ -252,7 +252,7 @@ Use in CLI: premium-coding
Name: free-combo
Models:
1. if/kimi-k2 (unlimited)
2. qw/coder-model (unlimited)
2. kr/qwen3-coder-next (free fallback)
Cost: $0 forever!
```
@@ -576,8 +576,6 @@ For the full environment variable reference, see the [README](../README.md).
**Qoder (`if/`)** — FREE OAuth: `if/kimi-k2-0905`, `if/kimi-k2`, `if/qwen3-coder-plus`, `if/qwen3-max`, `if/qwen3-max-preview`, `if/qwen3-vl-plus`, `if/qwen3-32b`, `if/qwen3-235b-a22b-thinking-2507`, `if/qwen3-235b-a22b-instruct`, `if/qwen3-235b`, `if/deepseek-v3.2`, `if/deepseek-v3`, `if/deepseek-r1`, `if/qoder-rome-30ba3b`
**Qwen (`qw/`)** — FREE OAuth (chat.qwen.ai): `qw/coder-model`, `qw/vision-model`
**GLM (`glm/`, `glm-cn/`, `zai/`, `glmt/`)** — $0.20.6/1M: `glm/glm-5.1`, `glm/glm-5`, `glm/glm-5-turbo`, `glm/glm-4.7`, `glm/glm-4.7-flash`, `glm/glm-4.6`, `glm/glm-4.6v`, `glm/glm-4.5`, `glm/glm-4.5v`, `glm/glm-4.5-air`
**MiniMax (`minimax/`, `minimax-cn/`)** — $0.2/1M: `minimax/MiniMax-M2.7`, `minimax/MiniMax-M2.7-highspeed`, `minimax/MiniMax-M2.5`, `minimax/MiniMax-M2.5-highspeed`

View File

@@ -364,9 +364,9 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **pool**, **notion**, *
### Provider Categories
**Free Providers (4):** Qoder AI, Qwen Code (deprecated), Kiro AI, Windsurf
**Free Providers (3):** Qoder AI, Kiro AI, Windsurf
**OAuth Providers (14):** Claude Code, Antigravity, OpenAI Codex, GitHub Copilot, Cursor IDE, Kimi Coding, Kilo Code, Cline, Qwen, Kiro, Qoder, Gemini, Windsurf, GitLab Duo
**OAuth Providers (13):** Claude Code, Antigravity, OpenAI Codex, GitHub Copilot, Cursor IDE, Kimi Coding, Kilo Code, Cline, Kiro, Qoder, Gemini, Windsurf, GitLab Duo
**API Key Providers (48+):** OpenAI, Anthropic, Gemini (Google AI Studio), DeepSeek, Groq, xAI (Grok), Mistral, Perplexity, Together AI, Fireworks AI, Cerebras, Cohere, NVIDIA NIM, Nebius AI, SiliconFlow, Hyperbolic, HuggingFace, OpenRouter, Vertex AI, Cloudflare Workers AI, Scaleway AI, AI/ML API, Pollinations AI, Puter AI, LongCat AI, Alibaba, Alibaba (China), Kimi, Kimi Coding (API Key), Minimax, Minimax (China), Blackbox AI, Synthetic, Kilo Gateway, Z.AI, GLM Coding, Deepgram, AssemblyAI, ElevenLabs, Cartesia, PlayHT, Inworld, NanoBanana, SD WebUI, ComfyUI, Ollama Cloud, Perplexity Search, Serper Search, Brave Search, Exa Search, Tavily Search, OpenCode Zen, OpenCode Go, Alibaba Coding Plan

View File

@@ -364,9 +364,9 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **pool**, **notion**, *
### Provider Categories
**Free Providers (4):** Qoder AI, Qwen Code (deprecated), Kiro AI, Windsurf
**Free Providers (3):** Qoder AI, Kiro AI, Windsurf
**OAuth Providers (14):** Claude Code, Antigravity, OpenAI Codex, GitHub Copilot, Cursor IDE, Kimi Coding, Kilo Code, Cline, Qwen, Kiro, Qoder, Gemini, Windsurf, GitLab Duo
**OAuth Providers (13):** Claude Code, Antigravity, OpenAI Codex, GitHub Copilot, Cursor IDE, Kimi Coding, Kilo Code, Cline, Kiro, Qoder, Gemini, Windsurf, GitLab Duo
**API Key Providers (48+):** OpenAI, Anthropic, Gemini (Google AI Studio), DeepSeek, Groq, xAI (Grok), Mistral, Perplexity, Together AI, Fireworks AI, Cerebras, Cohere, NVIDIA NIM, Nebius AI, SiliconFlow, Hyperbolic, HuggingFace, OpenRouter, Vertex AI, Cloudflare Workers AI, Scaleway AI, AI/ML API, Pollinations AI, Puter AI, LongCat AI, Alibaba, Alibaba (China), Kimi, Kimi Coding (API Key), Minimax, Minimax (China), Blackbox AI, Synthetic, Kilo Gateway, Z.AI, GLM Coding, Deepgram, AssemblyAI, ElevenLabs, Cartesia, PlayHT, Inworld, NanoBanana, SD WebUI, ComfyUI, Ollama Cloud, Perplexity Search, Serper Search, Brave Search, Exa Search, Tavily Search, OpenCode Zen, OpenCode Go, Alibaba Coding Plan

View File

@@ -364,9 +364,9 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **pool**, **notion**, *
### Provider Categories
**Free Providers (4):** Qoder AI, Qwen Code (deprecated), Kiro AI, Windsurf
**Free Providers (3):** Qoder AI, Kiro AI, Windsurf
**OAuth Providers (14):** Claude Code, Antigravity, OpenAI Codex, GitHub Copilot, Cursor IDE, Kimi Coding, Kilo Code, Cline, Qwen, Kiro, Qoder, Gemini, Windsurf, GitLab Duo
**OAuth Providers (13):** Claude Code, Antigravity, OpenAI Codex, GitHub Copilot, Cursor IDE, Kimi Coding, Kilo Code, Cline, Kiro, Qoder, Gemini, Windsurf, GitLab Duo
**API Key Providers (48+):** OpenAI, Anthropic, Gemini (Google AI Studio), DeepSeek, Groq, xAI (Grok), Mistral, Perplexity, Together AI, Fireworks AI, Cerebras, Cohere, NVIDIA NIM, Nebius AI, SiliconFlow, Hyperbolic, HuggingFace, OpenRouter, Vertex AI, Cloudflare Workers AI, Scaleway AI, AI/ML API, Pollinations AI, Puter AI, LongCat AI, Alibaba, Alibaba (China), Kimi, Kimi Coding (API Key), Minimax, Minimax (China), Blackbox AI, Synthetic, Kilo Gateway, Z.AI, GLM Coding, Deepgram, AssemblyAI, ElevenLabs, Cartesia, PlayHT, Inworld, NanoBanana, SD WebUI, ComfyUI, Ollama Cloud, Perplexity Search, Serper Search, Brave Search, Exa Search, Tavily Search, OpenCode Zen, OpenCode Go, Alibaba Coding Plan

View File

@@ -364,9 +364,9 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **pool**, **notion**, *
### Provider Categories
**Free Providers (4):** Qoder AI, Qwen Code (deprecated), Kiro AI, Windsurf
**Free Providers (3):** Qoder AI, Kiro AI, Windsurf
**OAuth Providers (14):** Claude Code, Antigravity, OpenAI Codex, GitHub Copilot, Cursor IDE, Kimi Coding, Kilo Code, Cline, Qwen, Kiro, Qoder, Gemini, Windsurf, GitLab Duo
**OAuth Providers (13):** Claude Code, Antigravity, OpenAI Codex, GitHub Copilot, Cursor IDE, Kimi Coding, Kilo Code, Cline, Kiro, Qoder, Gemini, Windsurf, GitLab Duo
**API Key Providers (48+):** OpenAI, Anthropic, Gemini (Google AI Studio), DeepSeek, Groq, xAI (Grok), Mistral, Perplexity, Together AI, Fireworks AI, Cerebras, Cohere, NVIDIA NIM, Nebius AI, SiliconFlow, Hyperbolic, HuggingFace, OpenRouter, Vertex AI, Cloudflare Workers AI, Scaleway AI, AI/ML API, Pollinations AI, Puter AI, LongCat AI, Alibaba, Alibaba (China), Kimi, Kimi Coding (API Key), Minimax, Minimax (China), Blackbox AI, Synthetic, Kilo Gateway, Z.AI, GLM Coding, Deepgram, AssemblyAI, ElevenLabs, Cartesia, PlayHT, Inworld, NanoBanana, SD WebUI, ComfyUI, Ollama Cloud, Perplexity Search, Serper Search, Brave Search, Exa Search, Tavily Search, OpenCode Zen, OpenCode Go, Alibaba Coding Plan

View File

@@ -364,9 +364,9 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **pool**, **notion**, *
### Provider Categories
**Free Providers (4):** Qoder AI, Qwen Code (deprecated), Kiro AI, Windsurf
**Free Providers (3):** Qoder AI, Kiro AI, Windsurf
**OAuth Providers (14):** Claude Code, Antigravity, OpenAI Codex, GitHub Copilot, Cursor IDE, Kimi Coding, Kilo Code, Cline, Qwen, Kiro, Qoder, Gemini, Windsurf, GitLab Duo
**OAuth Providers (13):** Claude Code, Antigravity, OpenAI Codex, GitHub Copilot, Cursor IDE, Kimi Coding, Kilo Code, Cline, Kiro, Qoder, Gemini, Windsurf, GitLab Duo
**API Key Providers (48+):** OpenAI, Anthropic, Gemini (Google AI Studio), DeepSeek, Groq, xAI (Grok), Mistral, Perplexity, Together AI, Fireworks AI, Cerebras, Cohere, NVIDIA NIM, Nebius AI, SiliconFlow, Hyperbolic, HuggingFace, OpenRouter, Vertex AI, Cloudflare Workers AI, Scaleway AI, AI/ML API, Pollinations AI, Puter AI, LongCat AI, Alibaba, Alibaba (China), Kimi, Kimi Coding (API Key), Minimax, Minimax (China), Blackbox AI, Synthetic, Kilo Gateway, Z.AI, GLM Coding, Deepgram, AssemblyAI, ElevenLabs, Cartesia, PlayHT, Inworld, NanoBanana, SD WebUI, ComfyUI, Ollama Cloud, Perplexity Search, Serper Search, Brave Search, Exa Search, Tavily Search, OpenCode Zen, OpenCode Go, Alibaba Coding Plan

View File

@@ -364,9 +364,9 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **pool**, **notion**, *
### Provider Categories
**Free Providers (4):** Qoder AI, Qwen Code (deprecated), Kiro AI, Windsurf
**Free Providers (3):** Qoder AI, Kiro AI, Windsurf
**OAuth Providers (14):** Claude Code, Antigravity, OpenAI Codex, GitHub Copilot, Cursor IDE, Kimi Coding, Kilo Code, Cline, Qwen, Kiro, Qoder, Gemini, Windsurf, GitLab Duo
**OAuth Providers (13):** Claude Code, Antigravity, OpenAI Codex, GitHub Copilot, Cursor IDE, Kimi Coding, Kilo Code, Cline, Kiro, Qoder, Gemini, Windsurf, GitLab Duo
**API Key Providers (48+):** OpenAI, Anthropic, Gemini (Google AI Studio), DeepSeek, Groq, xAI (Grok), Mistral, Perplexity, Together AI, Fireworks AI, Cerebras, Cohere, NVIDIA NIM, Nebius AI, SiliconFlow, Hyperbolic, HuggingFace, OpenRouter, Vertex AI, Cloudflare Workers AI, Scaleway AI, AI/ML API, Pollinations AI, Puter AI, LongCat AI, Alibaba, Alibaba (China), Kimi, Kimi Coding (API Key), Minimax, Minimax (China), Blackbox AI, Synthetic, Kilo Gateway, Z.AI, GLM Coding, Deepgram, AssemblyAI, ElevenLabs, Cartesia, PlayHT, Inworld, NanoBanana, SD WebUI, ComfyUI, Ollama Cloud, Perplexity Search, Serper Search, Brave Search, Exa Search, Tavily Search, OpenCode Zen, OpenCode Go, Alibaba Coding Plan

View File

@@ -364,9 +364,9 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **pool**, **notion**, *
### Provider Categories
**Free Providers (4):** Qoder AI, Qwen Code (deprecated), Kiro AI, Windsurf
**Free Providers (3):** Qoder AI, Kiro AI, Windsurf
**OAuth Providers (14):** Claude Code, Antigravity, OpenAI Codex, GitHub Copilot, Cursor IDE, Kimi Coding, Kilo Code, Cline, Qwen, Kiro, Qoder, Gemini, Windsurf, GitLab Duo
**OAuth Providers (13):** Claude Code, Antigravity, OpenAI Codex, GitHub Copilot, Cursor IDE, Kimi Coding, Kilo Code, Cline, Kiro, Qoder, Gemini, Windsurf, GitLab Duo
**API Key Providers (48+):** OpenAI, Anthropic, Gemini (Google AI Studio), DeepSeek, Groq, xAI (Grok), Mistral, Perplexity, Together AI, Fireworks AI, Cerebras, Cohere, NVIDIA NIM, Nebius AI, SiliconFlow, Hyperbolic, HuggingFace, OpenRouter, Vertex AI, Cloudflare Workers AI, Scaleway AI, AI/ML API, Pollinations AI, Puter AI, LongCat AI, Alibaba, Alibaba (China), Kimi, Kimi Coding (API Key), Minimax, Minimax (China), Blackbox AI, Synthetic, Kilo Gateway, Z.AI, GLM Coding, Deepgram, AssemblyAI, ElevenLabs, Cartesia, PlayHT, Inworld, NanoBanana, SD WebUI, ComfyUI, Ollama Cloud, Perplexity Search, Serper Search, Brave Search, Exa Search, Tavily Search, OpenCode Zen, OpenCode Go, Alibaba Coding Plan

View File

@@ -364,9 +364,9 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **pool**, **notion**, *
### Provider Categories
**Free Providers (4):** Qoder AI, Qwen Code (deprecated), Kiro AI, Windsurf
**Free Providers (3):** Qoder AI, Kiro AI, Windsurf
**OAuth Providers (14):** Claude Code, Antigravity, OpenAI Codex, GitHub Copilot, Cursor IDE, Kimi Coding, Kilo Code, Cline, Qwen, Kiro, Qoder, Gemini, Windsurf, GitLab Duo
**OAuth Providers (13):** Claude Code, Antigravity, OpenAI Codex, GitHub Copilot, Cursor IDE, Kimi Coding, Kilo Code, Cline, Kiro, Qoder, Gemini, Windsurf, GitLab Duo
**API Key Providers (48+):** OpenAI, Anthropic, Gemini (Google AI Studio), DeepSeek, Groq, xAI (Grok), Mistral, Perplexity, Together AI, Fireworks AI, Cerebras, Cohere, NVIDIA NIM, Nebius AI, SiliconFlow, Hyperbolic, HuggingFace, OpenRouter, Vertex AI, Cloudflare Workers AI, Scaleway AI, AI/ML API, Pollinations AI, Puter AI, LongCat AI, Alibaba, Alibaba (China), Kimi, Kimi Coding (API Key), Minimax, Minimax (China), Blackbox AI, Synthetic, Kilo Gateway, Z.AI, GLM Coding, Deepgram, AssemblyAI, ElevenLabs, Cartesia, PlayHT, Inworld, NanoBanana, SD WebUI, ComfyUI, Ollama Cloud, Perplexity Search, Serper Search, Brave Search, Exa Search, Tavily Search, OpenCode Zen, OpenCode Go, Alibaba Coding Plan

View File

@@ -364,9 +364,9 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **pool**, **notion**, *
### Provider Categories
**Free Providers (4):** Qoder AI, Qwen Code (deprecated), Kiro AI, Windsurf
**Free Providers (3):** Qoder AI, Kiro AI, Windsurf
**OAuth Providers (14):** Claude Code, Antigravity, OpenAI Codex, GitHub Copilot, Cursor IDE, Kimi Coding, Kilo Code, Cline, Qwen, Kiro, Qoder, Gemini, Windsurf, GitLab Duo
**OAuth Providers (13):** Claude Code, Antigravity, OpenAI Codex, GitHub Copilot, Cursor IDE, Kimi Coding, Kilo Code, Cline, Kiro, Qoder, Gemini, Windsurf, GitLab Duo
**API Key Providers (48+):** OpenAI, Anthropic, Gemini (Google AI Studio), DeepSeek, Groq, xAI (Grok), Mistral, Perplexity, Together AI, Fireworks AI, Cerebras, Cohere, NVIDIA NIM, Nebius AI, SiliconFlow, Hyperbolic, HuggingFace, OpenRouter, Vertex AI, Cloudflare Workers AI, Scaleway AI, AI/ML API, Pollinations AI, Puter AI, LongCat AI, Alibaba, Alibaba (China), Kimi, Kimi Coding (API Key), Minimax, Minimax (China), Blackbox AI, Synthetic, Kilo Gateway, Z.AI, GLM Coding, Deepgram, AssemblyAI, ElevenLabs, Cartesia, PlayHT, Inworld, NanoBanana, SD WebUI, ComfyUI, Ollama Cloud, Perplexity Search, Serper Search, Brave Search, Exa Search, Tavily Search, OpenCode Zen, OpenCode Go, Alibaba Coding Plan

View File

@@ -364,9 +364,9 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **pool**, **notion**, *
### Provider Categories
**Free Providers (4):** Qoder AI, Qwen Code (deprecated), Kiro AI, Windsurf
**Free Providers (3):** Qoder AI, Kiro AI, Windsurf
**OAuth Providers (14):** Claude Code, Antigravity, OpenAI Codex, GitHub Copilot, Cursor IDE, Kimi Coding, Kilo Code, Cline, Qwen, Kiro, Qoder, Gemini, Windsurf, GitLab Duo
**OAuth Providers (13):** Claude Code, Antigravity, OpenAI Codex, GitHub Copilot, Cursor IDE, Kimi Coding, Kilo Code, Cline, Kiro, Qoder, Gemini, Windsurf, GitLab Duo
**API Key Providers (48+):** OpenAI, Anthropic, Gemini (Google AI Studio), DeepSeek, Groq, xAI (Grok), Mistral, Perplexity, Together AI, Fireworks AI, Cerebras, Cohere, NVIDIA NIM, Nebius AI, SiliconFlow, Hyperbolic, HuggingFace, OpenRouter, Vertex AI, Cloudflare Workers AI, Scaleway AI, AI/ML API, Pollinations AI, Puter AI, LongCat AI, Alibaba, Alibaba (China), Kimi, Kimi Coding (API Key), Minimax, Minimax (China), Blackbox AI, Synthetic, Kilo Gateway, Z.AI, GLM Coding, Deepgram, AssemblyAI, ElevenLabs, Cartesia, PlayHT, Inworld, NanoBanana, SD WebUI, ComfyUI, Ollama Cloud, Perplexity Search, Serper Search, Brave Search, Exa Search, Tavily Search, OpenCode Zen, OpenCode Go, Alibaba Coding Plan

View File

@@ -364,9 +364,9 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **pool**, **notion**, *
### Provider Categories
**Free Providers (4):** Qoder AI, Qwen Code (deprecated), Kiro AI, Windsurf
**Free Providers (3):** Qoder AI, Kiro AI, Windsurf
**OAuth Providers (14):** Claude Code, Antigravity, OpenAI Codex, GitHub Copilot, Cursor IDE, Kimi Coding, Kilo Code, Cline, Qwen, Kiro, Qoder, Gemini, Windsurf, GitLab Duo
**OAuth Providers (13):** Claude Code, Antigravity, OpenAI Codex, GitHub Copilot, Cursor IDE, Kimi Coding, Kilo Code, Cline, Kiro, Qoder, Gemini, Windsurf, GitLab Duo
**API Key Providers (48+):** OpenAI, Anthropic, Gemini (Google AI Studio), DeepSeek, Groq, xAI (Grok), Mistral, Perplexity, Together AI, Fireworks AI, Cerebras, Cohere, NVIDIA NIM, Nebius AI, SiliconFlow, Hyperbolic, HuggingFace, OpenRouter, Vertex AI, Cloudflare Workers AI, Scaleway AI, AI/ML API, Pollinations AI, Puter AI, LongCat AI, Alibaba, Alibaba (China), Kimi, Kimi Coding (API Key), Minimax, Minimax (China), Blackbox AI, Synthetic, Kilo Gateway, Z.AI, GLM Coding, Deepgram, AssemblyAI, ElevenLabs, Cartesia, PlayHT, Inworld, NanoBanana, SD WebUI, ComfyUI, Ollama Cloud, Perplexity Search, Serper Search, Brave Search, Exa Search, Tavily Search, OpenCode Zen, OpenCode Go, Alibaba Coding Plan

View File

@@ -364,9 +364,9 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **pool**, **notion**, *
### Provider Categories
**Free Providers (4):** Qoder AI, Qwen Code (deprecated), Kiro AI, Windsurf
**Free Providers (3):** Qoder AI, Kiro AI, Windsurf
**OAuth Providers (14):** Claude Code, Antigravity, OpenAI Codex, GitHub Copilot, Cursor IDE, Kimi Coding, Kilo Code, Cline, Qwen, Kiro, Qoder, Gemini, Windsurf, GitLab Duo
**OAuth Providers (13):** Claude Code, Antigravity, OpenAI Codex, GitHub Copilot, Cursor IDE, Kimi Coding, Kilo Code, Cline, Kiro, Qoder, Gemini, Windsurf, GitLab Duo
**API Key Providers (48+):** OpenAI, Anthropic, Gemini (Google AI Studio), DeepSeek, Groq, xAI (Grok), Mistral, Perplexity, Together AI, Fireworks AI, Cerebras, Cohere, NVIDIA NIM, Nebius AI, SiliconFlow, Hyperbolic, HuggingFace, OpenRouter, Vertex AI, Cloudflare Workers AI, Scaleway AI, AI/ML API, Pollinations AI, Puter AI, LongCat AI, Alibaba, Alibaba (China), Kimi, Kimi Coding (API Key), Minimax, Minimax (China), Blackbox AI, Synthetic, Kilo Gateway, Z.AI, GLM Coding, Deepgram, AssemblyAI, ElevenLabs, Cartesia, PlayHT, Inworld, NanoBanana, SD WebUI, ComfyUI, Ollama Cloud, Perplexity Search, Serper Search, Brave Search, Exa Search, Tavily Search, OpenCode Zen, OpenCode Go, Alibaba Coding Plan

View File

@@ -364,9 +364,9 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **pool**, **notion**, *
### Provider Categories
**Free Providers (4):** Qoder AI, Qwen Code (deprecated), Kiro AI, Windsurf
**Free Providers (3):** Qoder AI, Kiro AI, Windsurf
**OAuth Providers (14):** Claude Code, Antigravity, OpenAI Codex, GitHub Copilot, Cursor IDE, Kimi Coding, Kilo Code, Cline, Qwen, Kiro, Qoder, Gemini, Windsurf, GitLab Duo
**OAuth Providers (13):** Claude Code, Antigravity, OpenAI Codex, GitHub Copilot, Cursor IDE, Kimi Coding, Kilo Code, Cline, Kiro, Qoder, Gemini, Windsurf, GitLab Duo
**API Key Providers (48+):** OpenAI, Anthropic, Gemini (Google AI Studio), DeepSeek, Groq, xAI (Grok), Mistral, Perplexity, Together AI, Fireworks AI, Cerebras, Cohere, NVIDIA NIM, Nebius AI, SiliconFlow, Hyperbolic, HuggingFace, OpenRouter, Vertex AI, Cloudflare Workers AI, Scaleway AI, AI/ML API, Pollinations AI, Puter AI, LongCat AI, Alibaba, Alibaba (China), Kimi, Kimi Coding (API Key), Minimax, Minimax (China), Blackbox AI, Synthetic, Kilo Gateway, Z.AI, GLM Coding, Deepgram, AssemblyAI, ElevenLabs, Cartesia, PlayHT, Inworld, NanoBanana, SD WebUI, ComfyUI, Ollama Cloud, Perplexity Search, Serper Search, Brave Search, Exa Search, Tavily Search, OpenCode Zen, OpenCode Go, Alibaba Coding Plan

View File

@@ -364,9 +364,9 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **pool**, **notion**, *
### Provider Categories
**Free Providers (4):** Qoder AI, Qwen Code (deprecated), Kiro AI, Windsurf
**Free Providers (3):** Qoder AI, Kiro AI, Windsurf
**OAuth Providers (14):** Claude Code, Antigravity, OpenAI Codex, GitHub Copilot, Cursor IDE, Kimi Coding, Kilo Code, Cline, Qwen, Kiro, Qoder, Gemini, Windsurf, GitLab Duo
**OAuth Providers (13):** Claude Code, Antigravity, OpenAI Codex, GitHub Copilot, Cursor IDE, Kimi Coding, Kilo Code, Cline, Kiro, Qoder, Gemini, Windsurf, GitLab Duo
**API Key Providers (48+):** OpenAI, Anthropic, Gemini (Google AI Studio), DeepSeek, Groq, xAI (Grok), Mistral, Perplexity, Together AI, Fireworks AI, Cerebras, Cohere, NVIDIA NIM, Nebius AI, SiliconFlow, Hyperbolic, HuggingFace, OpenRouter, Vertex AI, Cloudflare Workers AI, Scaleway AI, AI/ML API, Pollinations AI, Puter AI, LongCat AI, Alibaba, Alibaba (China), Kimi, Kimi Coding (API Key), Minimax, Minimax (China), Blackbox AI, Synthetic, Kilo Gateway, Z.AI, GLM Coding, Deepgram, AssemblyAI, ElevenLabs, Cartesia, PlayHT, Inworld, NanoBanana, SD WebUI, ComfyUI, Ollama Cloud, Perplexity Search, Serper Search, Brave Search, Exa Search, Tavily Search, OpenCode Zen, OpenCode Go, Alibaba Coding Plan

View File

@@ -364,9 +364,9 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **pool**, **notion**, *
### Provider Categories
**Free Providers (4):** Qoder AI, Qwen Code (deprecated), Kiro AI, Windsurf
**Free Providers (3):** Qoder AI, Kiro AI, Windsurf
**OAuth Providers (14):** Claude Code, Antigravity, OpenAI Codex, GitHub Copilot, Cursor IDE, Kimi Coding, Kilo Code, Cline, Qwen, Kiro, Qoder, Gemini, Windsurf, GitLab Duo
**OAuth Providers (13):** Claude Code, Antigravity, OpenAI Codex, GitHub Copilot, Cursor IDE, Kimi Coding, Kilo Code, Cline, Kiro, Qoder, Gemini, Windsurf, GitLab Duo
**API Key Providers (48+):** OpenAI, Anthropic, Gemini (Google AI Studio), DeepSeek, Groq, xAI (Grok), Mistral, Perplexity, Together AI, Fireworks AI, Cerebras, Cohere, NVIDIA NIM, Nebius AI, SiliconFlow, Hyperbolic, HuggingFace, OpenRouter, Vertex AI, Cloudflare Workers AI, Scaleway AI, AI/ML API, Pollinations AI, Puter AI, LongCat AI, Alibaba, Alibaba (China), Kimi, Kimi Coding (API Key), Minimax, Minimax (China), Blackbox AI, Synthetic, Kilo Gateway, Z.AI, GLM Coding, Deepgram, AssemblyAI, ElevenLabs, Cartesia, PlayHT, Inworld, NanoBanana, SD WebUI, ComfyUI, Ollama Cloud, Perplexity Search, Serper Search, Brave Search, Exa Search, Tavily Search, OpenCode Zen, OpenCode Go, Alibaba Coding Plan

View File

@@ -364,9 +364,9 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **pool**, **notion**, *
### Provider Categories
**Free Providers (4):** Qoder AI, Qwen Code (deprecated), Kiro AI, Windsurf
**Free Providers (3):** Qoder AI, Kiro AI, Windsurf
**OAuth Providers (14):** Claude Code, Antigravity, OpenAI Codex, GitHub Copilot, Cursor IDE, Kimi Coding, Kilo Code, Cline, Qwen, Kiro, Qoder, Gemini, Windsurf, GitLab Duo
**OAuth Providers (13):** Claude Code, Antigravity, OpenAI Codex, GitHub Copilot, Cursor IDE, Kimi Coding, Kilo Code, Cline, Kiro, Qoder, Gemini, Windsurf, GitLab Duo
**API Key Providers (48+):** OpenAI, Anthropic, Gemini (Google AI Studio), DeepSeek, Groq, xAI (Grok), Mistral, Perplexity, Together AI, Fireworks AI, Cerebras, Cohere, NVIDIA NIM, Nebius AI, SiliconFlow, Hyperbolic, HuggingFace, OpenRouter, Vertex AI, Cloudflare Workers AI, Scaleway AI, AI/ML API, Pollinations AI, Puter AI, LongCat AI, Alibaba, Alibaba (China), Kimi, Kimi Coding (API Key), Minimax, Minimax (China), Blackbox AI, Synthetic, Kilo Gateway, Z.AI, GLM Coding, Deepgram, AssemblyAI, ElevenLabs, Cartesia, PlayHT, Inworld, NanoBanana, SD WebUI, ComfyUI, Ollama Cloud, Perplexity Search, Serper Search, Brave Search, Exa Search, Tavily Search, OpenCode Zen, OpenCode Go, Alibaba Coding Plan

View File

@@ -364,9 +364,9 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **pool**, **notion**, *
### Provider Categories
**Free Providers (4):** Qoder AI, Qwen Code (deprecated), Kiro AI, Windsurf
**Free Providers (3):** Qoder AI, Kiro AI, Windsurf
**OAuth Providers (14):** Claude Code, Antigravity, OpenAI Codex, GitHub Copilot, Cursor IDE, Kimi Coding, Kilo Code, Cline, Qwen, Kiro, Qoder, Gemini, Windsurf, GitLab Duo
**OAuth Providers (13):** Claude Code, Antigravity, OpenAI Codex, GitHub Copilot, Cursor IDE, Kimi Coding, Kilo Code, Cline, Kiro, Qoder, Gemini, Windsurf, GitLab Duo
**API Key Providers (48+):** OpenAI, Anthropic, Gemini (Google AI Studio), DeepSeek, Groq, xAI (Grok), Mistral, Perplexity, Together AI, Fireworks AI, Cerebras, Cohere, NVIDIA NIM, Nebius AI, SiliconFlow, Hyperbolic, HuggingFace, OpenRouter, Vertex AI, Cloudflare Workers AI, Scaleway AI, AI/ML API, Pollinations AI, Puter AI, LongCat AI, Alibaba, Alibaba (China), Kimi, Kimi Coding (API Key), Minimax, Minimax (China), Blackbox AI, Synthetic, Kilo Gateway, Z.AI, GLM Coding, Deepgram, AssemblyAI, ElevenLabs, Cartesia, PlayHT, Inworld, NanoBanana, SD WebUI, ComfyUI, Ollama Cloud, Perplexity Search, Serper Search, Brave Search, Exa Search, Tavily Search, OpenCode Zen, OpenCode Go, Alibaba Coding Plan

View File

@@ -364,9 +364,9 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **pool**, **notion**, *
### Provider Categories
**Free Providers (4):** Qoder AI, Qwen Code (deprecated), Kiro AI, Windsurf
**Free Providers (3):** Qoder AI, Kiro AI, Windsurf
**OAuth Providers (14):** Claude Code, Antigravity, OpenAI Codex, GitHub Copilot, Cursor IDE, Kimi Coding, Kilo Code, Cline, Qwen, Kiro, Qoder, Gemini, Windsurf, GitLab Duo
**OAuth Providers (13):** Claude Code, Antigravity, OpenAI Codex, GitHub Copilot, Cursor IDE, Kimi Coding, Kilo Code, Cline, Kiro, Qoder, Gemini, Windsurf, GitLab Duo
**API Key Providers (48+):** OpenAI, Anthropic, Gemini (Google AI Studio), DeepSeek, Groq, xAI (Grok), Mistral, Perplexity, Together AI, Fireworks AI, Cerebras, Cohere, NVIDIA NIM, Nebius AI, SiliconFlow, Hyperbolic, HuggingFace, OpenRouter, Vertex AI, Cloudflare Workers AI, Scaleway AI, AI/ML API, Pollinations AI, Puter AI, LongCat AI, Alibaba, Alibaba (China), Kimi, Kimi Coding (API Key), Minimax, Minimax (China), Blackbox AI, Synthetic, Kilo Gateway, Z.AI, GLM Coding, Deepgram, AssemblyAI, ElevenLabs, Cartesia, PlayHT, Inworld, NanoBanana, SD WebUI, ComfyUI, Ollama Cloud, Perplexity Search, Serper Search, Brave Search, Exa Search, Tavily Search, OpenCode Zen, OpenCode Go, Alibaba Coding Plan

View File

@@ -364,9 +364,9 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **pool**, **notion**, *
### Provider Categories
**Free Providers (4):** Qoder AI, Qwen Code (deprecated), Kiro AI, Windsurf
**Free Providers (3):** Qoder AI, Kiro AI, Windsurf
**OAuth Providers (14):** Claude Code, Antigravity, OpenAI Codex, GitHub Copilot, Cursor IDE, Kimi Coding, Kilo Code, Cline, Qwen, Kiro, Qoder, Gemini, Windsurf, GitLab Duo
**OAuth Providers (13):** Claude Code, Antigravity, OpenAI Codex, GitHub Copilot, Cursor IDE, Kimi Coding, Kilo Code, Cline, Kiro, Qoder, Gemini, Windsurf, GitLab Duo
**API Key Providers (48+):** OpenAI, Anthropic, Gemini (Google AI Studio), DeepSeek, Groq, xAI (Grok), Mistral, Perplexity, Together AI, Fireworks AI, Cerebras, Cohere, NVIDIA NIM, Nebius AI, SiliconFlow, Hyperbolic, HuggingFace, OpenRouter, Vertex AI, Cloudflare Workers AI, Scaleway AI, AI/ML API, Pollinations AI, Puter AI, LongCat AI, Alibaba, Alibaba (China), Kimi, Kimi Coding (API Key), Minimax, Minimax (China), Blackbox AI, Synthetic, Kilo Gateway, Z.AI, GLM Coding, Deepgram, AssemblyAI, ElevenLabs, Cartesia, PlayHT, Inworld, NanoBanana, SD WebUI, ComfyUI, Ollama Cloud, Perplexity Search, Serper Search, Brave Search, Exa Search, Tavily Search, OpenCode Zen, OpenCode Go, Alibaba Coding Plan

View File

@@ -364,9 +364,9 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **pool**, **notion**, *
### Provider Categories
**Free Providers (4):** Qoder AI, Qwen Code (deprecated), Kiro AI, Windsurf
**Free Providers (3):** Qoder AI, Kiro AI, Windsurf
**OAuth Providers (14):** Claude Code, Antigravity, OpenAI Codex, GitHub Copilot, Cursor IDE, Kimi Coding, Kilo Code, Cline, Qwen, Kiro, Qoder, Gemini, Windsurf, GitLab Duo
**OAuth Providers (13):** Claude Code, Antigravity, OpenAI Codex, GitHub Copilot, Cursor IDE, Kimi Coding, Kilo Code, Cline, Kiro, Qoder, Gemini, Windsurf, GitLab Duo
**API Key Providers (48+):** OpenAI, Anthropic, Gemini (Google AI Studio), DeepSeek, Groq, xAI (Grok), Mistral, Perplexity, Together AI, Fireworks AI, Cerebras, Cohere, NVIDIA NIM, Nebius AI, SiliconFlow, Hyperbolic, HuggingFace, OpenRouter, Vertex AI, Cloudflare Workers AI, Scaleway AI, AI/ML API, Pollinations AI, Puter AI, LongCat AI, Alibaba, Alibaba (China), Kimi, Kimi Coding (API Key), Minimax, Minimax (China), Blackbox AI, Synthetic, Kilo Gateway, Z.AI, GLM Coding, Deepgram, AssemblyAI, ElevenLabs, Cartesia, PlayHT, Inworld, NanoBanana, SD WebUI, ComfyUI, Ollama Cloud, Perplexity Search, Serper Search, Brave Search, Exa Search, Tavily Search, OpenCode Zen, OpenCode Go, Alibaba Coding Plan

View File

@@ -364,9 +364,9 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **pool**, **notion**, *
### Provider Categories
**Free Providers (4):** Qoder AI, Qwen Code (deprecated), Kiro AI, Windsurf
**Free Providers (3):** Qoder AI, Kiro AI, Windsurf
**OAuth Providers (14):** Claude Code, Antigravity, OpenAI Codex, GitHub Copilot, Cursor IDE, Kimi Coding, Kilo Code, Cline, Qwen, Kiro, Qoder, Gemini, Windsurf, GitLab Duo
**OAuth Providers (13):** Claude Code, Antigravity, OpenAI Codex, GitHub Copilot, Cursor IDE, Kimi Coding, Kilo Code, Cline, Kiro, Qoder, Gemini, Windsurf, GitLab Duo
**API Key Providers (48+):** OpenAI, Anthropic, Gemini (Google AI Studio), DeepSeek, Groq, xAI (Grok), Mistral, Perplexity, Together AI, Fireworks AI, Cerebras, Cohere, NVIDIA NIM, Nebius AI, SiliconFlow, Hyperbolic, HuggingFace, OpenRouter, Vertex AI, Cloudflare Workers AI, Scaleway AI, AI/ML API, Pollinations AI, Puter AI, LongCat AI, Alibaba, Alibaba (China), Kimi, Kimi Coding (API Key), Minimax, Minimax (China), Blackbox AI, Synthetic, Kilo Gateway, Z.AI, GLM Coding, Deepgram, AssemblyAI, ElevenLabs, Cartesia, PlayHT, Inworld, NanoBanana, SD WebUI, ComfyUI, Ollama Cloud, Perplexity Search, Serper Search, Brave Search, Exa Search, Tavily Search, OpenCode Zen, OpenCode Go, Alibaba Coding Plan

View File

@@ -364,9 +364,9 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **pool**, **notion**, *
### Provider Categories
**Free Providers (4):** Qoder AI, Qwen Code (deprecated), Kiro AI, Windsurf
**Free Providers (3):** Qoder AI, Kiro AI, Windsurf
**OAuth Providers (14):** Claude Code, Antigravity, OpenAI Codex, GitHub Copilot, Cursor IDE, Kimi Coding, Kilo Code, Cline, Qwen, Kiro, Qoder, Gemini, Windsurf, GitLab Duo
**OAuth Providers (13):** Claude Code, Antigravity, OpenAI Codex, GitHub Copilot, Cursor IDE, Kimi Coding, Kilo Code, Cline, Kiro, Qoder, Gemini, Windsurf, GitLab Duo
**API Key Providers (48+):** OpenAI, Anthropic, Gemini (Google AI Studio), DeepSeek, Groq, xAI (Grok), Mistral, Perplexity, Together AI, Fireworks AI, Cerebras, Cohere, NVIDIA NIM, Nebius AI, SiliconFlow, Hyperbolic, HuggingFace, OpenRouter, Vertex AI, Cloudflare Workers AI, Scaleway AI, AI/ML API, Pollinations AI, Puter AI, LongCat AI, Alibaba, Alibaba (China), Kimi, Kimi Coding (API Key), Minimax, Minimax (China), Blackbox AI, Synthetic, Kilo Gateway, Z.AI, GLM Coding, Deepgram, AssemblyAI, ElevenLabs, Cartesia, PlayHT, Inworld, NanoBanana, SD WebUI, ComfyUI, Ollama Cloud, Perplexity Search, Serper Search, Brave Search, Exa Search, Tavily Search, OpenCode Zen, OpenCode Go, Alibaba Coding Plan

View File

@@ -364,9 +364,9 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **pool**, **notion**, *
### Provider Categories
**Free Providers (4):** Qoder AI, Qwen Code (deprecated), Kiro AI, Windsurf
**Free Providers (3):** Qoder AI, Kiro AI, Windsurf
**OAuth Providers (14):** Claude Code, Antigravity, OpenAI Codex, GitHub Copilot, Cursor IDE, Kimi Coding, Kilo Code, Cline, Qwen, Kiro, Qoder, Gemini, Windsurf, GitLab Duo
**OAuth Providers (13):** Claude Code, Antigravity, OpenAI Codex, GitHub Copilot, Cursor IDE, Kimi Coding, Kilo Code, Cline, Kiro, Qoder, Gemini, Windsurf, GitLab Duo
**API Key Providers (48+):** OpenAI, Anthropic, Gemini (Google AI Studio), DeepSeek, Groq, xAI (Grok), Mistral, Perplexity, Together AI, Fireworks AI, Cerebras, Cohere, NVIDIA NIM, Nebius AI, SiliconFlow, Hyperbolic, HuggingFace, OpenRouter, Vertex AI, Cloudflare Workers AI, Scaleway AI, AI/ML API, Pollinations AI, Puter AI, LongCat AI, Alibaba, Alibaba (China), Kimi, Kimi Coding (API Key), Minimax, Minimax (China), Blackbox AI, Synthetic, Kilo Gateway, Z.AI, GLM Coding, Deepgram, AssemblyAI, ElevenLabs, Cartesia, PlayHT, Inworld, NanoBanana, SD WebUI, ComfyUI, Ollama Cloud, Perplexity Search, Serper Search, Brave Search, Exa Search, Tavily Search, OpenCode Zen, OpenCode Go, Alibaba Coding Plan

View File

@@ -364,9 +364,9 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **pool**, **notion**, *
### Provider Categories
**Free Providers (4):** Qoder AI, Qwen Code (deprecated), Kiro AI, Windsurf
**Free Providers (3):** Qoder AI, Kiro AI, Windsurf
**OAuth Providers (14):** Claude Code, Antigravity, OpenAI Codex, GitHub Copilot, Cursor IDE, Kimi Coding, Kilo Code, Cline, Qwen, Kiro, Qoder, Gemini, Windsurf, GitLab Duo
**OAuth Providers (13):** Claude Code, Antigravity, OpenAI Codex, GitHub Copilot, Cursor IDE, Kimi Coding, Kilo Code, Cline, Kiro, Qoder, Gemini, Windsurf, GitLab Duo
**API Key Providers (48+):** OpenAI, Anthropic, Gemini (Google AI Studio), DeepSeek, Groq, xAI (Grok), Mistral, Perplexity, Together AI, Fireworks AI, Cerebras, Cohere, NVIDIA NIM, Nebius AI, SiliconFlow, Hyperbolic, HuggingFace, OpenRouter, Vertex AI, Cloudflare Workers AI, Scaleway AI, AI/ML API, Pollinations AI, Puter AI, LongCat AI, Alibaba, Alibaba (China), Kimi, Kimi Coding (API Key), Minimax, Minimax (China), Blackbox AI, Synthetic, Kilo Gateway, Z.AI, GLM Coding, Deepgram, AssemblyAI, ElevenLabs, Cartesia, PlayHT, Inworld, NanoBanana, SD WebUI, ComfyUI, Ollama Cloud, Perplexity Search, Serper Search, Brave Search, Exa Search, Tavily Search, OpenCode Zen, OpenCode Go, Alibaba Coding Plan

View File

@@ -364,9 +364,9 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **pool**, **notion**, *
### Provider Categories
**Free Providers (4):** Qoder AI, Qwen Code (deprecated), Kiro AI, Windsurf
**Free Providers (3):** Qoder AI, Kiro AI, Windsurf
**OAuth Providers (14):** Claude Code, Antigravity, OpenAI Codex, GitHub Copilot, Cursor IDE, Kimi Coding, Kilo Code, Cline, Qwen, Kiro, Qoder, Gemini, Windsurf, GitLab Duo
**OAuth Providers (13):** Claude Code, Antigravity, OpenAI Codex, GitHub Copilot, Cursor IDE, Kimi Coding, Kilo Code, Cline, Kiro, Qoder, Gemini, Windsurf, GitLab Duo
**API Key Providers (48+):** OpenAI, Anthropic, Gemini (Google AI Studio), DeepSeek, Groq, xAI (Grok), Mistral, Perplexity, Together AI, Fireworks AI, Cerebras, Cohere, NVIDIA NIM, Nebius AI, SiliconFlow, Hyperbolic, HuggingFace, OpenRouter, Vertex AI, Cloudflare Workers AI, Scaleway AI, AI/ML API, Pollinations AI, Puter AI, LongCat AI, Alibaba, Alibaba (China), Kimi, Kimi Coding (API Key), Minimax, Minimax (China), Blackbox AI, Synthetic, Kilo Gateway, Z.AI, GLM Coding, Deepgram, AssemblyAI, ElevenLabs, Cartesia, PlayHT, Inworld, NanoBanana, SD WebUI, ComfyUI, Ollama Cloud, Perplexity Search, Serper Search, Brave Search, Exa Search, Tavily Search, OpenCode Zen, OpenCode Go, Alibaba Coding Plan

View File

@@ -364,9 +364,9 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **pool**, **notion**, *
### Provider Categories
**Free Providers (4):** Qoder AI, Qwen Code (deprecated), Kiro AI, Windsurf
**Free Providers (3):** Qoder AI, Kiro AI, Windsurf
**OAuth Providers (14):** Claude Code, Antigravity, OpenAI Codex, GitHub Copilot, Cursor IDE, Kimi Coding, Kilo Code, Cline, Qwen, Kiro, Qoder, Gemini, Windsurf, GitLab Duo
**OAuth Providers (13):** Claude Code, Antigravity, OpenAI Codex, GitHub Copilot, Cursor IDE, Kimi Coding, Kilo Code, Cline, Kiro, Qoder, Gemini, Windsurf, GitLab Duo
**API Key Providers (48+):** OpenAI, Anthropic, Gemini (Google AI Studio), DeepSeek, Groq, xAI (Grok), Mistral, Perplexity, Together AI, Fireworks AI, Cerebras, Cohere, NVIDIA NIM, Nebius AI, SiliconFlow, Hyperbolic, HuggingFace, OpenRouter, Vertex AI, Cloudflare Workers AI, Scaleway AI, AI/ML API, Pollinations AI, Puter AI, LongCat AI, Alibaba, Alibaba (China), Kimi, Kimi Coding (API Key), Minimax, Minimax (China), Blackbox AI, Synthetic, Kilo Gateway, Z.AI, GLM Coding, Deepgram, AssemblyAI, ElevenLabs, Cartesia, PlayHT, Inworld, NanoBanana, SD WebUI, ComfyUI, Ollama Cloud, Perplexity Search, Serper Search, Brave Search, Exa Search, Tavily Search, OpenCode Zen, OpenCode Go, Alibaba Coding Plan

View File

@@ -364,9 +364,9 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **pool**, **notion**, *
### Provider Categories
**Free Providers (4):** Qoder AI, Qwen Code (deprecated), Kiro AI, Windsurf
**Free Providers (3):** Qoder AI, Kiro AI, Windsurf
**OAuth Providers (14):** Claude Code, Antigravity, OpenAI Codex, GitHub Copilot, Cursor IDE, Kimi Coding, Kilo Code, Cline, Qwen, Kiro, Qoder, Gemini, Windsurf, GitLab Duo
**OAuth Providers (13):** Claude Code, Antigravity, OpenAI Codex, GitHub Copilot, Cursor IDE, Kimi Coding, Kilo Code, Cline, Kiro, Qoder, Gemini, Windsurf, GitLab Duo
**API Key Providers (48+):** OpenAI, Anthropic, Gemini (Google AI Studio), DeepSeek, Groq, xAI (Grok), Mistral, Perplexity, Together AI, Fireworks AI, Cerebras, Cohere, NVIDIA NIM, Nebius AI, SiliconFlow, Hyperbolic, HuggingFace, OpenRouter, Vertex AI, Cloudflare Workers AI, Scaleway AI, AI/ML API, Pollinations AI, Puter AI, LongCat AI, Alibaba, Alibaba (China), Kimi, Kimi Coding (API Key), Minimax, Minimax (China), Blackbox AI, Synthetic, Kilo Gateway, Z.AI, GLM Coding, Deepgram, AssemblyAI, ElevenLabs, Cartesia, PlayHT, Inworld, NanoBanana, SD WebUI, ComfyUI, Ollama Cloud, Perplexity Search, Serper Search, Brave Search, Exa Search, Tavily Search, OpenCode Zen, OpenCode Go, Alibaba Coding Plan

View File

@@ -364,9 +364,9 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **pool**, **notion**, *
### Provider Categories
**Free Providers (4):** Qoder AI, Qwen Code (deprecated), Kiro AI, Windsurf
**Free Providers (3):** Qoder AI, Kiro AI, Windsurf
**OAuth Providers (14):** Claude Code, Antigravity, OpenAI Codex, GitHub Copilot, Cursor IDE, Kimi Coding, Kilo Code, Cline, Qwen, Kiro, Qoder, Gemini, Windsurf, GitLab Duo
**OAuth Providers (13):** Claude Code, Antigravity, OpenAI Codex, GitHub Copilot, Cursor IDE, Kimi Coding, Kilo Code, Cline, Kiro, Qoder, Gemini, Windsurf, GitLab Duo
**API Key Providers (48+):** OpenAI, Anthropic, Gemini (Google AI Studio), DeepSeek, Groq, xAI (Grok), Mistral, Perplexity, Together AI, Fireworks AI, Cerebras, Cohere, NVIDIA NIM, Nebius AI, SiliconFlow, Hyperbolic, HuggingFace, OpenRouter, Vertex AI, Cloudflare Workers AI, Scaleway AI, AI/ML API, Pollinations AI, Puter AI, LongCat AI, Alibaba, Alibaba (China), Kimi, Kimi Coding (API Key), Minimax, Minimax (China), Blackbox AI, Synthetic, Kilo Gateway, Z.AI, GLM Coding, Deepgram, AssemblyAI, ElevenLabs, Cartesia, PlayHT, Inworld, NanoBanana, SD WebUI, ComfyUI, Ollama Cloud, Perplexity Search, Serper Search, Brave Search, Exa Search, Tavily Search, OpenCode Zen, OpenCode Go, Alibaba Coding Plan

View File

@@ -364,9 +364,9 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **pool**, **notion**, *
### Provider Categories
**Free Providers (4):** Qoder AI, Qwen Code (deprecated), Kiro AI, Windsurf
**Free Providers (3):** Qoder AI, Kiro AI, Windsurf
**OAuth Providers (14):** Claude Code, Antigravity, OpenAI Codex, GitHub Copilot, Cursor IDE, Kimi Coding, Kilo Code, Cline, Qwen, Kiro, Qoder, Gemini, Windsurf, GitLab Duo
**OAuth Providers (13):** Claude Code, Antigravity, OpenAI Codex, GitHub Copilot, Cursor IDE, Kimi Coding, Kilo Code, Cline, Kiro, Qoder, Gemini, Windsurf, GitLab Duo
**API Key Providers (48+):** OpenAI, Anthropic, Gemini (Google AI Studio), DeepSeek, Groq, xAI (Grok), Mistral, Perplexity, Together AI, Fireworks AI, Cerebras, Cohere, NVIDIA NIM, Nebius AI, SiliconFlow, Hyperbolic, HuggingFace, OpenRouter, Vertex AI, Cloudflare Workers AI, Scaleway AI, AI/ML API, Pollinations AI, Puter AI, LongCat AI, Alibaba, Alibaba (China), Kimi, Kimi Coding (API Key), Minimax, Minimax (China), Blackbox AI, Synthetic, Kilo Gateway, Z.AI, GLM Coding, Deepgram, AssemblyAI, ElevenLabs, Cartesia, PlayHT, Inworld, NanoBanana, SD WebUI, ComfyUI, Ollama Cloud, Perplexity Search, Serper Search, Brave Search, Exa Search, Tavily Search, OpenCode Zen, OpenCode Go, Alibaba Coding Plan

View File

@@ -364,9 +364,9 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **pool**, **notion**, *
### Provider Categories
**Free Providers (4):** Qoder AI, Qwen Code (deprecated), Kiro AI, Windsurf
**Free Providers (3):** Qoder AI, Kiro AI, Windsurf
**OAuth Providers (14):** Claude Code, Antigravity, OpenAI Codex, GitHub Copilot, Cursor IDE, Kimi Coding, Kilo Code, Cline, Qwen, Kiro, Qoder, Gemini, Windsurf, GitLab Duo
**OAuth Providers (13):** Claude Code, Antigravity, OpenAI Codex, GitHub Copilot, Cursor IDE, Kimi Coding, Kilo Code, Cline, Kiro, Qoder, Gemini, Windsurf, GitLab Duo
**API Key Providers (48+):** OpenAI, Anthropic, Gemini (Google AI Studio), DeepSeek, Groq, xAI (Grok), Mistral, Perplexity, Together AI, Fireworks AI, Cerebras, Cohere, NVIDIA NIM, Nebius AI, SiliconFlow, Hyperbolic, HuggingFace, OpenRouter, Vertex AI, Cloudflare Workers AI, Scaleway AI, AI/ML API, Pollinations AI, Puter AI, LongCat AI, Alibaba, Alibaba (China), Kimi, Kimi Coding (API Key), Minimax, Minimax (China), Blackbox AI, Synthetic, Kilo Gateway, Z.AI, GLM Coding, Deepgram, AssemblyAI, ElevenLabs, Cartesia, PlayHT, Inworld, NanoBanana, SD WebUI, ComfyUI, Ollama Cloud, Perplexity Search, Serper Search, Brave Search, Exa Search, Tavily Search, OpenCode Zen, OpenCode Go, Alibaba Coding Plan

View File

@@ -364,9 +364,9 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **pool**, **notion**, *
### Provider Categories
**Free Providers (4):** Qoder AI, Qwen Code (deprecated), Kiro AI, Windsurf
**Free Providers (3):** Qoder AI, Kiro AI, Windsurf
**OAuth Providers (14):** Claude Code, Antigravity, OpenAI Codex, GitHub Copilot, Cursor IDE, Kimi Coding, Kilo Code, Cline, Qwen, Kiro, Qoder, Gemini, Windsurf, GitLab Duo
**OAuth Providers (13):** Claude Code, Antigravity, OpenAI Codex, GitHub Copilot, Cursor IDE, Kimi Coding, Kilo Code, Cline, Kiro, Qoder, Gemini, Windsurf, GitLab Duo
**API Key Providers (48+):** OpenAI, Anthropic, Gemini (Google AI Studio), DeepSeek, Groq, xAI (Grok), Mistral, Perplexity, Together AI, Fireworks AI, Cerebras, Cohere, NVIDIA NIM, Nebius AI, SiliconFlow, Hyperbolic, HuggingFace, OpenRouter, Vertex AI, Cloudflare Workers AI, Scaleway AI, AI/ML API, Pollinations AI, Puter AI, LongCat AI, Alibaba, Alibaba (China), Kimi, Kimi Coding (API Key), Minimax, Minimax (China), Blackbox AI, Synthetic, Kilo Gateway, Z.AI, GLM Coding, Deepgram, AssemblyAI, ElevenLabs, Cartesia, PlayHT, Inworld, NanoBanana, SD WebUI, ComfyUI, Ollama Cloud, Perplexity Search, Serper Search, Brave Search, Exa Search, Tavily Search, OpenCode Zen, OpenCode Go, Alibaba Coding Plan

View File

@@ -364,9 +364,9 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **pool**, **notion**, *
### Provider Categories
**Free Providers (4):** Qoder AI, Qwen Code (deprecated), Kiro AI, Windsurf
**Free Providers (3):** Qoder AI, Kiro AI, Windsurf
**OAuth Providers (14):** Claude Code, Antigravity, OpenAI Codex, GitHub Copilot, Cursor IDE, Kimi Coding, Kilo Code, Cline, Qwen, Kiro, Qoder, Gemini, Windsurf, GitLab Duo
**OAuth Providers (13):** Claude Code, Antigravity, OpenAI Codex, GitHub Copilot, Cursor IDE, Kimi Coding, Kilo Code, Cline, Kiro, Qoder, Gemini, Windsurf, GitLab Duo
**API Key Providers (48+):** OpenAI, Anthropic, Gemini (Google AI Studio), DeepSeek, Groq, xAI (Grok), Mistral, Perplexity, Together AI, Fireworks AI, Cerebras, Cohere, NVIDIA NIM, Nebius AI, SiliconFlow, Hyperbolic, HuggingFace, OpenRouter, Vertex AI, Cloudflare Workers AI, Scaleway AI, AI/ML API, Pollinations AI, Puter AI, LongCat AI, Alibaba, Alibaba (China), Kimi, Kimi Coding (API Key), Minimax, Minimax (China), Blackbox AI, Synthetic, Kilo Gateway, Z.AI, GLM Coding, Deepgram, AssemblyAI, ElevenLabs, Cartesia, PlayHT, Inworld, NanoBanana, SD WebUI, ComfyUI, Ollama Cloud, Perplexity Search, Serper Search, Brave Search, Exa Search, Tavily Search, OpenCode Zen, OpenCode Go, Alibaba Coding Plan

View File

@@ -364,9 +364,9 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **pool**, **notion**, *
### Provider Categories
**Free Providers (4):** Qoder AI, Qwen Code (deprecated), Kiro AI, Windsurf
**Free Providers (3):** Qoder AI, Kiro AI, Windsurf
**OAuth Providers (14):** Claude Code, Antigravity, OpenAI Codex, GitHub Copilot, Cursor IDE, Kimi Coding, Kilo Code, Cline, Qwen, Kiro, Qoder, Gemini, Windsurf, GitLab Duo
**OAuth Providers (13):** Claude Code, Antigravity, OpenAI Codex, GitHub Copilot, Cursor IDE, Kimi Coding, Kilo Code, Cline, Kiro, Qoder, Gemini, Windsurf, GitLab Duo
**API Key Providers (48+):** OpenAI, Anthropic, Gemini (Google AI Studio), DeepSeek, Groq, xAI (Grok), Mistral, Perplexity, Together AI, Fireworks AI, Cerebras, Cohere, NVIDIA NIM, Nebius AI, SiliconFlow, Hyperbolic, HuggingFace, OpenRouter, Vertex AI, Cloudflare Workers AI, Scaleway AI, AI/ML API, Pollinations AI, Puter AI, LongCat AI, Alibaba, Alibaba (China), Kimi, Kimi Coding (API Key), Minimax, Minimax (China), Blackbox AI, Synthetic, Kilo Gateway, Z.AI, GLM Coding, Deepgram, AssemblyAI, ElevenLabs, Cartesia, PlayHT, Inworld, NanoBanana, SD WebUI, ComfyUI, Ollama Cloud, Perplexity Search, Serper Search, Brave Search, Exa Search, Tavily Search, OpenCode Zen, OpenCode Go, Alibaba Coding Plan

View File

@@ -364,9 +364,9 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **pool**, **notion**, *
### Provider Categories
**Free Providers (4):** Qoder AI, Qwen Code (deprecated), Kiro AI, Windsurf
**Free Providers (3):** Qoder AI, Kiro AI, Windsurf
**OAuth Providers (14):** Claude Code, Antigravity, OpenAI Codex, GitHub Copilot, Cursor IDE, Kimi Coding, Kilo Code, Cline, Qwen, Kiro, Qoder, Gemini, Windsurf, GitLab Duo
**OAuth Providers (13):** Claude Code, Antigravity, OpenAI Codex, GitHub Copilot, Cursor IDE, Kimi Coding, Kilo Code, Cline, Kiro, Qoder, Gemini, Windsurf, GitLab Duo
**API Key Providers (48+):** OpenAI, Anthropic, Gemini (Google AI Studio), DeepSeek, Groq, xAI (Grok), Mistral, Perplexity, Together AI, Fireworks AI, Cerebras, Cohere, NVIDIA NIM, Nebius AI, SiliconFlow, Hyperbolic, HuggingFace, OpenRouter, Vertex AI, Cloudflare Workers AI, Scaleway AI, AI/ML API, Pollinations AI, Puter AI, LongCat AI, Alibaba, Alibaba (China), Kimi, Kimi Coding (API Key), Minimax, Minimax (China), Blackbox AI, Synthetic, Kilo Gateway, Z.AI, GLM Coding, Deepgram, AssemblyAI, ElevenLabs, Cartesia, PlayHT, Inworld, NanoBanana, SD WebUI, ComfyUI, Ollama Cloud, Perplexity Search, Serper Search, Brave Search, Exa Search, Tavily Search, OpenCode Zen, OpenCode Go, Alibaba Coding Plan

View File

@@ -364,9 +364,9 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **pool**, **notion**, *
### Provider Categories
**Free Providers (4):** Qoder AI, Qwen Code (deprecated), Kiro AI, Windsurf
**Free Providers (3):** Qoder AI, Kiro AI, Windsurf
**OAuth Providers (14):** Claude Code, Antigravity, OpenAI Codex, GitHub Copilot, Cursor IDE, Kimi Coding, Kilo Code, Cline, Qwen, Kiro, Qoder, Gemini, Windsurf, GitLab Duo
**OAuth Providers (13):** Claude Code, Antigravity, OpenAI Codex, GitHub Copilot, Cursor IDE, Kimi Coding, Kilo Code, Cline, Kiro, Qoder, Gemini, Windsurf, GitLab Duo
**API Key Providers (48+):** OpenAI, Anthropic, Gemini (Google AI Studio), DeepSeek, Groq, xAI (Grok), Mistral, Perplexity, Together AI, Fireworks AI, Cerebras, Cohere, NVIDIA NIM, Nebius AI, SiliconFlow, Hyperbolic, HuggingFace, OpenRouter, Vertex AI, Cloudflare Workers AI, Scaleway AI, AI/ML API, Pollinations AI, Puter AI, LongCat AI, Alibaba, Alibaba (China), Kimi, Kimi Coding (API Key), Minimax, Minimax (China), Blackbox AI, Synthetic, Kilo Gateway, Z.AI, GLM Coding, Deepgram, AssemblyAI, ElevenLabs, Cartesia, PlayHT, Inworld, NanoBanana, SD WebUI, ComfyUI, Ollama Cloud, Perplexity Search, Serper Search, Brave Search, Exa Search, Tavily Search, OpenCode Zen, OpenCode Go, Alibaba Coding Plan

View File

@@ -364,9 +364,9 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **pool**, **notion**, *
### Provider Categories
**Free Providers (4):** Qoder AI, Qwen Code (deprecated), Kiro AI, Windsurf
**Free Providers (3):** Qoder AI, Kiro AI, Windsurf
**OAuth Providers (14):** Claude Code, Antigravity, OpenAI Codex, GitHub Copilot, Cursor IDE, Kimi Coding, Kilo Code, Cline, Qwen, Kiro, Qoder, Gemini, Windsurf, GitLab Duo
**OAuth Providers (13):** Claude Code, Antigravity, OpenAI Codex, GitHub Copilot, Cursor IDE, Kimi Coding, Kilo Code, Cline, Kiro, Qoder, Gemini, Windsurf, GitLab Duo
**API Key Providers (48+):** OpenAI, Anthropic, Gemini (Google AI Studio), DeepSeek, Groq, xAI (Grok), Mistral, Perplexity, Together AI, Fireworks AI, Cerebras, Cohere, NVIDIA NIM, Nebius AI, SiliconFlow, Hyperbolic, HuggingFace, OpenRouter, Vertex AI, Cloudflare Workers AI, Scaleway AI, AI/ML API, Pollinations AI, Puter AI, LongCat AI, Alibaba, Alibaba (China), Kimi, Kimi Coding (API Key), Minimax, Minimax (China), Blackbox AI, Synthetic, Kilo Gateway, Z.AI, GLM Coding, Deepgram, AssemblyAI, ElevenLabs, Cartesia, PlayHT, Inworld, NanoBanana, SD WebUI, ComfyUI, Ollama Cloud, Perplexity Search, Serper Search, Brave Search, Exa Search, Tavily Search, OpenCode Zen, OpenCode Go, Alibaba Coding Plan

View File

@@ -364,9 +364,9 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **pool**, **notion**, *
### Provider Categories
**Free Providers (4):** Qoder AI, Qwen Code (deprecated), Kiro AI, Windsurf
**Free Providers (3):** Qoder AI, Kiro AI, Windsurf
**OAuth Providers (14):** Claude Code, Antigravity, OpenAI Codex, GitHub Copilot, Cursor IDE, Kimi Coding, Kilo Code, Cline, Qwen, Kiro, Qoder, Gemini, Windsurf, GitLab Duo
**OAuth Providers (13):** Claude Code, Antigravity, OpenAI Codex, GitHub Copilot, Cursor IDE, Kimi Coding, Kilo Code, Cline, Kiro, Qoder, Gemini, Windsurf, GitLab Duo
**API Key Providers (48+):** OpenAI, Anthropic, Gemini (Google AI Studio), DeepSeek, Groq, xAI (Grok), Mistral, Perplexity, Together AI, Fireworks AI, Cerebras, Cohere, NVIDIA NIM, Nebius AI, SiliconFlow, Hyperbolic, HuggingFace, OpenRouter, Vertex AI, Cloudflare Workers AI, Scaleway AI, AI/ML API, Pollinations AI, Puter AI, LongCat AI, Alibaba, Alibaba (China), Kimi, Kimi Coding (API Key), Minimax, Minimax (China), Blackbox AI, Synthetic, Kilo Gateway, Z.AI, GLM Coding, Deepgram, AssemblyAI, ElevenLabs, Cartesia, PlayHT, Inworld, NanoBanana, SD WebUI, ComfyUI, Ollama Cloud, Perplexity Search, Serper Search, Brave Search, Exa Search, Tavily Search, OpenCode Zen, OpenCode Go, Alibaba Coding Plan

View File

@@ -364,9 +364,9 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **pool**, **notion**, *
### Provider Categories
**Free Providers (4):** Qoder AI, Qwen Code (deprecated), Kiro AI, Windsurf
**Free Providers (3):** Qoder AI, Kiro AI, Windsurf
**OAuth Providers (14):** Claude Code, Antigravity, OpenAI Codex, GitHub Copilot, Cursor IDE, Kimi Coding, Kilo Code, Cline, Qwen, Kiro, Qoder, Gemini, Windsurf, GitLab Duo
**OAuth Providers (13):** Claude Code, Antigravity, OpenAI Codex, GitHub Copilot, Cursor IDE, Kimi Coding, Kilo Code, Cline, Kiro, Qoder, Gemini, Windsurf, GitLab Duo
**API Key Providers (48+):** OpenAI, Anthropic, Gemini (Google AI Studio), DeepSeek, Groq, xAI (Grok), Mistral, Perplexity, Together AI, Fireworks AI, Cerebras, Cohere, NVIDIA NIM, Nebius AI, SiliconFlow, Hyperbolic, HuggingFace, OpenRouter, Vertex AI, Cloudflare Workers AI, Scaleway AI, AI/ML API, Pollinations AI, Puter AI, LongCat AI, Alibaba, Alibaba (China), Kimi, Kimi Coding (API Key), Minimax, Minimax (China), Blackbox AI, Synthetic, Kilo Gateway, Z.AI, GLM Coding, Deepgram, AssemblyAI, ElevenLabs, Cartesia, PlayHT, Inworld, NanoBanana, SD WebUI, ComfyUI, Ollama Cloud, Perplexity Search, Serper Search, Brave Search, Exa Search, Tavily Search, OpenCode Zen, OpenCode Go, Alibaba Coding Plan

View File

@@ -364,9 +364,9 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **pool**, **notion**, *
### Provider Categories
**Free Providers (4):** Qoder AI, Qwen Code (deprecated), Kiro AI, Windsurf
**Free Providers (3):** Qoder AI, Kiro AI, Windsurf
**OAuth Providers (14):** Claude Code, Antigravity, OpenAI Codex, GitHub Copilot, Cursor IDE, Kimi Coding, Kilo Code, Cline, Qwen, Kiro, Qoder, Gemini, Windsurf, GitLab Duo
**OAuth Providers (13):** Claude Code, Antigravity, OpenAI Codex, GitHub Copilot, Cursor IDE, Kimi Coding, Kilo Code, Cline, Kiro, Qoder, Gemini, Windsurf, GitLab Duo
**API Key Providers (48+):** OpenAI, Anthropic, Gemini (Google AI Studio), DeepSeek, Groq, xAI (Grok), Mistral, Perplexity, Together AI, Fireworks AI, Cerebras, Cohere, NVIDIA NIM, Nebius AI, SiliconFlow, Hyperbolic, HuggingFace, OpenRouter, Vertex AI, Cloudflare Workers AI, Scaleway AI, AI/ML API, Pollinations AI, Puter AI, LongCat AI, Alibaba, Alibaba (China), Kimi, Kimi Coding (API Key), Minimax, Minimax (China), Blackbox AI, Synthetic, Kilo Gateway, Z.AI, GLM Coding, Deepgram, AssemblyAI, ElevenLabs, Cartesia, PlayHT, Inworld, NanoBanana, SD WebUI, ComfyUI, Ollama Cloud, Perplexity Search, Serper Search, Brave Search, Exa Search, Tavily Search, OpenCode Zen, OpenCode Go, Alibaba Coding Plan

View File

@@ -364,9 +364,9 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **pool**, **notion**, *
### Provider Categories
**Free Providers (4):** Qoder AI, Qwen Code (deprecated), Kiro AI, Windsurf
**Free Providers (3):** Qoder AI, Kiro AI, Windsurf
**OAuth Providers (14):** Claude Code, Antigravity, OpenAI Codex, GitHub Copilot, Cursor IDE, Kimi Coding, Kilo Code, Cline, Qwen, Kiro, Qoder, Gemini, Windsurf, GitLab Duo
**OAuth Providers (13):** Claude Code, Antigravity, OpenAI Codex, GitHub Copilot, Cursor IDE, Kimi Coding, Kilo Code, Cline, Kiro, Qoder, Gemini, Windsurf, GitLab Duo
**API Key Providers (48+):** OpenAI, Anthropic, Gemini (Google AI Studio), DeepSeek, Groq, xAI (Grok), Mistral, Perplexity, Together AI, Fireworks AI, Cerebras, Cohere, NVIDIA NIM, Nebius AI, SiliconFlow, Hyperbolic, HuggingFace, OpenRouter, Vertex AI, Cloudflare Workers AI, Scaleway AI, AI/ML API, Pollinations AI, Puter AI, LongCat AI, Alibaba, Alibaba (China), Kimi, Kimi Coding (API Key), Minimax, Minimax (China), Blackbox AI, Synthetic, Kilo Gateway, Z.AI, GLM Coding, Deepgram, AssemblyAI, ElevenLabs, Cartesia, PlayHT, Inworld, NanoBanana, SD WebUI, ComfyUI, Ollama Cloud, Perplexity Search, Serper Search, Brave Search, Exa Search, Tavily Search, OpenCode Zen, OpenCode Go, Alibaba Coding Plan

View File

@@ -364,9 +364,9 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **pool**, **notion**, *
### Provider Categories
**Free Providers (4):** Qoder AI, Qwen Code (deprecated), Kiro AI, Windsurf
**Free Providers (3):** Qoder AI, Kiro AI, Windsurf
**OAuth Providers (14):** Claude Code, Antigravity, OpenAI Codex, GitHub Copilot, Cursor IDE, Kimi Coding, Kilo Code, Cline, Qwen, Kiro, Qoder, Gemini, Windsurf, GitLab Duo
**OAuth Providers (13):** Claude Code, Antigravity, OpenAI Codex, GitHub Copilot, Cursor IDE, Kimi Coding, Kilo Code, Cline, Kiro, Qoder, Gemini, Windsurf, GitLab Duo
**API Key Providers (48+):** OpenAI, Anthropic, Gemini (Google AI Studio), DeepSeek, Groq, xAI (Grok), Mistral, Perplexity, Together AI, Fireworks AI, Cerebras, Cohere, NVIDIA NIM, Nebius AI, SiliconFlow, Hyperbolic, HuggingFace, OpenRouter, Vertex AI, Cloudflare Workers AI, Scaleway AI, AI/ML API, Pollinations AI, Puter AI, LongCat AI, Alibaba, Alibaba (China), Kimi, Kimi Coding (API Key), Minimax, Minimax (China), Blackbox AI, Synthetic, Kilo Gateway, Z.AI, GLM Coding, Deepgram, AssemblyAI, ElevenLabs, Cartesia, PlayHT, Inworld, NanoBanana, SD WebUI, ComfyUI, Ollama Cloud, Perplexity Search, Serper Search, Brave Search, Exa Search, Tavily Search, OpenCode Zen, OpenCode Go, Alibaba Coding Plan

View File

@@ -364,9 +364,9 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **pool**, **notion**, *
### Provider Categories
**Free Providers (4):** Qoder AI, Qwen Code (deprecated), Kiro AI, Windsurf
**Free Providers (3):** Qoder AI, Kiro AI, Windsurf
**OAuth Providers (14):** Claude Code, Antigravity, OpenAI Codex, GitHub Copilot, Cursor IDE, Kimi Coding, Kilo Code, Cline, Qwen, Kiro, Qoder, Gemini, Windsurf, GitLab Duo
**OAuth Providers (13):** Claude Code, Antigravity, OpenAI Codex, GitHub Copilot, Cursor IDE, Kimi Coding, Kilo Code, Cline, Kiro, Qoder, Gemini, Windsurf, GitLab Duo
**API Key Providers (48+):** OpenAI, Anthropic, Gemini (Google AI Studio), DeepSeek, Groq, xAI (Grok), Mistral, Perplexity, Together AI, Fireworks AI, Cerebras, Cohere, NVIDIA NIM, Nebius AI, SiliconFlow, Hyperbolic, HuggingFace, OpenRouter, Vertex AI, Cloudflare Workers AI, Scaleway AI, AI/ML API, Pollinations AI, Puter AI, LongCat AI, Alibaba, Alibaba (China), Kimi, Kimi Coding (API Key), Minimax, Minimax (China), Blackbox AI, Synthetic, Kilo Gateway, Z.AI, GLM Coding, Deepgram, AssemblyAI, ElevenLabs, Cartesia, PlayHT, Inworld, NanoBanana, SD WebUI, ComfyUI, Ollama Cloud, Perplexity Search, Serper Search, Brave Search, Exa Search, Tavily Search, OpenCode Zen, OpenCode Go, Alibaba Coding Plan

View File

@@ -12,7 +12,7 @@ OmniRoute integrates with three categories of CLI tools spread across three dedi
| Page | Route | Concept | Count |
| -------------- | ----------------------- | ------------------------------------------------------------------------- | ------------ |
| **CLI Code's** | `/dashboard/cli-code` | Coding tools you point at OmniRoute (Client → CLI → OmniRoute → Provider) | 20 |
| **CLI Code's** | `/dashboard/cli-code` | Coding tools you point at OmniRoute (Client → CLI → OmniRoute → Provider) | 21 |
| **CLI Agents** | `/dashboard/cli-agents` | Autonomous agents you point at OmniRoute (same flow, broader scope) | 6 |
| **ACP Agents** | `/dashboard/acp-agents` | CLIs that OmniRoute spawns as backend via stdio/ACP (reverse flow) | see registry |
@@ -60,7 +60,7 @@ omniroute setup-goose omniroute setup-qwen omniroute setup-aider
Each accepts `--remote <url> --api-key <key>` (configure a local tool against a
remote OmniRoute), `--dry-run` (preview without writing), and `--port`. Tools
without model auto-discovery (Cline, Kilo, Roo, Goose, Qwen, Aider, Gemini) take
without model auto-discovery (Cline, Kilo, Roo, Goose, Aider, Gemini) take
`--model <id>` (and `--yes` for non-interactive runs). The launchers
`omniroute launch` (Claude Code) and `omniroute launch-codex` (Codex) spawn the CLI
with the right env injected and write no config at all.
@@ -102,7 +102,6 @@ All tools that appear in `/dashboard/cli-code`. Those with `baseUrlSupport: none
| kilo | Kilo Code | Kilo-Org | full | custom | false |
| roo | Roo Code | Roo (OSS) | full | guide | false |
| continue | Continue | continue.dev | full | guide | false |
| qwen | Qwen Code | Alibaba | full | guide | true |
| aider | Aider | OSS (P. Gauthier) | full | guide | true |
| forge | ForgeCode | Antinomy HQ | full | custom | true |
| jcode | jcode | 1jehuang (OSS) | full | custom | false |
@@ -116,6 +115,7 @@ All tools that appear in `/dashboard/cli-code`. Those with `baseUrlSupport: none
| pi | Pi (pi-coding-agent) | M. Zechner (OSS) | full | custom | false |
| grok-build | Grok Build | xAI | full | custom | false |
| crush | Crush | OSS (Charm) | full | custom | false |
| qwen | Qwen Code | Alibaba | full | guide | true |
| cursor | Cursor | Anysphere | none | guide | false |
| antigravity | Antigravity | Google | none | mitm | false |
| hermes | Hermes | Nous Research | none | guide | false |
@@ -123,7 +123,6 @@ All tools that appear in `/dashboard/cli-code`. Those with `baseUrlSupport: none
| custom | Custom CLI | — | full | custom-builder | false |
Tools with `baseUrlSupport: "partial"` show a badge "⚠ Base URL parcial" in the dashboard card.
---
## 2. CLI Agents Catalog (8 tools)
@@ -147,7 +146,6 @@ Autonomous agents that appear in `/dashboard/cli-agents`:
This page (renamed from `/dashboard/agents`) shows CLIs that OmniRoute can **spawn** as backend execution engines via stdio/ACP protocol. The catalog is maintained separately in `src/lib/acp/registry.ts` and is **not** the same as `CLI_TOOLS`.
---
## 4. MITM Backlog (not shown in dashboard)
@@ -212,6 +210,7 @@ New tools with `configType: "custom"` have dedicated settings API routes:
| `POST /api/cli-tools/smelt-settings` | Smelt |
| `POST /api/cli-tools/pi-settings` | Pi coding agent |
| `POST /api/cli-tools/grok-build-settings` | Grok Build (~/.grok/config.toml, `[model.omniroute]`) |
| `POST /api/cli-tools/qwen-settings` | Qwen Code (`~/.qwen/settings.json` + dedicated `.env` key) |
All routes use `sanitizeErrorMessage()` for error responses (Hard Rule #12).
@@ -302,7 +301,7 @@ npm install -g cline
# KiloCode
npm install -g kilocode
# Qwen Code (Alibaba)
# Qwen Code
npm install -g @qwen-code/qwen-code
# Aider
@@ -532,55 +531,6 @@ under `/dashboard/cli-tools → Kiro`.
---
#### Qwen Code (Alibaba)
Qwen Code supports OpenAI-compatible API endpoints via environment variables or `settings.json`.
> Qwen OAuth free tier was discontinued on 2026-04-15. Use OmniRoute with
> `bailian-coding-plan` / `alibaba` / `alibaba-cn` / `openrouter` / `anthropic` /
> `gemini` providers instead.
**Option 1: Environment variables (`~/.qwen/.env`)**
```bash
mkdir -p ~/.qwen && cat > ~/.qwen/.env << EOF
OPENAI_API_KEY="sk-your-omniroute-key"
OPENAI_BASE_URL="http://localhost:20128/v1"
OPENAI_MODEL="auto"
EOF
```
**Option 2: `settings.json` with `security.auth`**
```json
// ~/.qwen/settings.json
{
"security": {
"auth": {
"selectedType": "openai",
"apiKey": "sk-your-omniroute-key",
"baseUrl": "http://localhost:20128/v1"
}
},
"model": {
"name": "claude-sonnet-4-6"
}
}
```
**Option 3: Inline CLI flags**
```bash
OPENAI_BASE_URL="http://localhost:20128/v1" \
OPENAI_API_KEY="sk-your-omniroute-key" \
OPENAI_MODEL="auto" \
qwen
```
> For a **remote server** replace `localhost:20128` with the server IP or domain.
---
## 10. Internal OmniRoute CLI
The `omniroute` binary provides commands for server lifecycle, setup, diagnostics, and provider management. Entry point: `bin/omniroute.mjs`.

View File

@@ -471,7 +471,6 @@ Built-in credentials for **localhost development**. For remote deployments, regi
| `CODEX_OAUTH_CLIENT_ID` | Codex / OpenAI | Public client. |
| `GEMINI_OAUTH_CLIENT_ID` | Gemini (Google) | Requires matching `_SECRET`. |
| `GEMINI_OAUTH_CLIENT_SECRET` | Gemini (Google) | — |
| `QWEN_OAUTH_CLIENT_ID` | Qwen (Alibaba) | Public client. |
| `KIMI_CODING_OAUTH_CLIENT_ID` | Kimi Coding (Moonshot) | Public client. |
| `ANTIGRAVITY_OAUTH_CLIENT_ID` | Antigravity (Google) | Requires matching `_SECRET`. |
| `ANTIGRAVITY_OAUTH_CLIENT_SECRET` | Antigravity (Google) | — |
@@ -530,7 +529,6 @@ process.env[`${PROVIDER_ID}_USER_AGENT`]
| `KIRO_OAUTH_CLIENT_ID` | `kiro-cli` | Override the Kiro social device-code `clientId` (public id) |
| `KIRO_VERIFY_FULL_CRC` | `false` | Opt-in: full per-frame message CRC validation on the Kiro event stream (debug corrupted streams) |
| `QODER_USER_AGENT` | `Qoder-Cli` | When Qoder CLI updates |
| `QWEN_USER_AGENT` | `QwenCode/0.19.3 (linux; x64)` | When Qwen Code updates |
| `CURSOR_USER_AGENT` | `Cursor/3.3` | When Cursor updates |
> [!TIP]
@@ -556,7 +554,6 @@ When enabled, OmniRoute reorders HTTP headers and JSON body fields to match the
| `CLI_COMPAT_KIMI_CODING` | `=1` | Mimics Kimi Coding request signature |
| `CLI_COMPAT_KILOCODE` | `=1` | Mimics Kilo Code request signature |
| `CLI_COMPAT_CLINE` | `=1` | Mimics Cline request signature |
| `CLI_COMPAT_QWEN` | `=1` | Mimics Qwen Code request signature |
### Global

View File

@@ -83,7 +83,7 @@ A 50-agent web-research pass (official docs + last-7-days news, adversarially ve
| `muse-spark-web` | Meta ToS explicitly prohibits automated access without prior permission, reverse engineering without written permission, and circumventing technologi… |
| `nlpcloud` | ToS explicitly prohibits "setting up a proxy or other device that allows others to access the Service through it" and grants only a non-transferable,… |
| `opencode` | ToS (Anomaly Innovations, Inc.) explicitly restricts use to "your own internal use, and not on behalf of or for the benefit of any third party" — ope… |
| `qwen-web` | The free OAuth tier is discontinued; no ToS permits a self-hosted proxy using session tokens against chat.qwen.ai. Even before shutdown, automated/pr… |
| `qwen-web` | No ToS permits a self-hosted proxy using session tokens against chat.qwen.ai; automated/programmatic access remains high-risk. |
| `t3-web` | ToS explicitly restricts accounts to personal use only, prohibits credential sharing with third parties, and bans automated/bot/scraping access — a s… |
### ✅ Generally permissive — caution / ambiguous / ok (the rest)
@@ -308,7 +308,7 @@ A 50-agent web-research pass (official docs + last-7-days news, adversarially ve
- **`publicai`** — The shipped freeNote ("Free community inference tier") is broadly accurate but understates the specificity: the 20 RPM rate limit is now documented. No major tightening found; the service remains fre…
- **`puter`** — Partially matches: the "500+ models" count is still accurate. However "users pay via Puter account" understates the reality — free accounts receive an undocumented starting credit that can be exhaust…
- **`qoder`** — Our catalog ships freeNote "(none)", but Qoder does have a free tier: a Community Edition with unlimited basic-model completions (daily-capped, unspecified limit) plus a one-time 14-day/300-credit Pr…
- **`qwen-web`** — The shipped freeNote ("Free — Qwen models via chat.qwen.ai with login token") is now stale. The login-token/OAuth free API path was terminated on 2026-04-15. The qwen-web executor will receive 401 er…
- **`qwen-web`** — Session-token access against chat.qwen.ai is not a dependable free-provider path and may be rejected upstream.
- **`sambanova`** — Our shipped note only described the one-time $5 credit (30-day validity). The current reality includes a permanent recurring free tier with documented rate limits (20 RPM, 20 RPD, 200k TPD) that pers…
- **`sensenova`** — Our shipped freeNote says "Free SenseTime models" which is vague but directionally correct — free access does exist. However, reality is more nuanced: free access is a time-limited public beta (Token…
- **`serper-search`** — The shipped freeNote says "(none)" which is partially accurate — there is no recurring free plan — but Serper does offer 2,500 one-time trial credits on signup. The catalog note could be more precise…

View File

@@ -54,7 +54,6 @@ Use the dashboard at `/dashboard/providers` to enable, configure, and test each
| `kimi-coding` | `kmc` | Kimi Code CLI | OAuth | — | Sign in with the same Kimi account used by Kimi Code CLI. OmniRoute uses the CLI OAuth flow and Kimi Coding Plan endpoints. |
| `kiro` | `kr` | Kiro AI | OAuth | — | Free tier: 50 credits/month (~25K100K tokens). ⚠️ Kiro ToS prohibits third-party proxy/harness use. |
| `qoder` | `if` | Qoder | OAuth | — | — |
| `qwen` | `qw` | Qwen Code | OAuth | — | ⚠️ **DEPRECATED.** Qwen OAuth free tier was discontinued on 2026-04-15. Use 'bailian-coding-plan', 'alibaba', 'alibaba-cn', or 'openrouter' provider with API key instead. |
| `trae` | `tr` | Trae | OAuth | [link](https://trae.ai) | Trae is an AI-native IDE by ByteDance (SOLO remote agent). Authorize via trae.ai in the popup, or sign in at solo.trae.ai and paste the Cloud-IDE-JWT (sent as 'Authorization: Cloud-IDE-JWT <token>', ~14-day lifetime) as the access token; web_id/biz_user_id/user_unique_id/scope/tenant/region propagate via providerSpecificData. No headless refresh for pasted tokens — re-paste on expiry. |
| `windsurf` | `ws` | Windsurf (Devin CLI) | OAuth | [link](https://windsurf.com) | In the Windsurf / VS Code IDE, open the command palette and run `Windsurf: Provide Auth Token` (or click the Jupyter "Get Windsurf Authentication Token" button), then copy the shown token and paste it here. Note: opening windsurf.com/show-auth-token directly only renders a "Redirecting" page — the IDE must initiate the flow (it adds a `?state=...` param) for the token to appear. |
| `xai-oauth` | `xao` | xAI OAuth (Grok) | OAuth | [link](https://x.ai) | Sign in with xAI to use api.x.ai models such as Grok 4.5. This is separate from Grok Build JWT sessions, which use cli-chat-proxy.grok.com and grok-build model aliases. |

View File

@@ -147,7 +147,7 @@ Touch points:
## CLI Fingerprint Registry — `open-sse/config/cliFingerprints.ts`
Per-provider table that pins **exact** header ordering and JSON body field ordering captured from mitmproxy traces of the official CLIs. Currently registered: `codex`, `claude`, plus runtime-derived profiles in `providerHeaderProfiles.ts` for `antigravity`, `qwen`, `github`.
Per-provider table that pins **exact** header ordering and JSON body field ordering captured from mitmproxy traces of the official CLIs. Currently registered: `codex`, `claude`, plus runtime-derived profiles in `providerHeaderProfiles.ts` for `antigravity` and `github`.
```ts
interface CliFingerprint {
@@ -220,7 +220,6 @@ All MITM endpoints require management auth (`requireCliToolsAuth`). The sudo pas
| `ANTIGRAVITY_USER_AGENT` | `antigravity/2.0.1 linux/arm64 google-api-nodejs-client/10.3.0` |
| `KIRO_USER_AGENT` | `AWS-SDK-JS/3.0.0 kiro-ide/1.0.0` |
| `QODER_USER_AGENT` | `Qoder-Cli` |
| `QWEN_USER_AGENT` | `QwenCode/0.19.3 (linux; x64)` |
| `CURSOR_USER_AGENT` | `Cursor/3.4` |
Consumed by `open-sse/executors/base.ts::buildHeaders()` via dynamic lookup. **Bump these when providers release new CLI versions** — stale UA strings start getting rejected as outdated clients.
@@ -238,7 +237,6 @@ Consumed by `open-sse/executors/base.ts::buildHeaders()` via dynamic lookup. **B
| `CLI_COMPAT_KIMI_CODING=1` | Kimi Coding |
| `CLI_COMPAT_KILOCODE=1` | KiloCode |
| `CLI_COMPAT_CLINE=1` | Cline |
| `CLI_COMPAT_QWEN=1` | Qwen Code |
| `CLI_COMPAT_ALL=1` | Enable all of the above |
The provider IP is **always preserved** — the toggle only reshapes the request wire image, it does not switch IP egress.

View File

@@ -360,9 +360,9 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **pool**, **notion**, *
### Provider Categories
**Free Providers (4):** Qoder AI, Qwen Code (deprecated), Kiro AI, Windsurf
**Free Providers (3):** Qoder AI, Kiro AI, Windsurf
**OAuth Providers (14):** Claude Code, Antigravity, OpenAI Codex, GitHub Copilot, Cursor IDE, Kimi Coding, Kilo Code, Cline, Qwen, Kiro, Qoder, Gemini, Windsurf, GitLab Duo
**OAuth Providers (13):** Claude Code, Antigravity, OpenAI Codex, GitHub Copilot, Cursor IDE, Kimi Coding, Kilo Code, Cline, Kiro, Qoder, Gemini, Windsurf, GitLab Duo
**API Key Providers (48+):** OpenAI, Anthropic, Gemini (Google AI Studio), DeepSeek, Groq, xAI (Grok), Mistral, Perplexity, Together AI, Fireworks AI, Cerebras, Cohere, NVIDIA NIM, Nebius AI, SiliconFlow, Hyperbolic, HuggingFace, OpenRouter, Vertex AI, Cloudflare Workers AI, Scaleway AI, AI/ML API, Pollinations AI, Puter AI, LongCat AI, Alibaba, Alibaba (China), Kimi, Kimi Coding (API Key), Minimax, Minimax (China), Blackbox AI, Synthetic, Kilo Gateway, Z.AI, GLM Coding, Deepgram, AssemblyAI, ElevenLabs, Cartesia, PlayHT, Inworld, NanoBanana, SD WebUI, ComfyUI, Ollama Cloud, Perplexity Search, Serper Search, Brave Search, Exa Search, Tavily Search, OpenCode Zen, OpenCode Go, Alibaba Coding Plan

View File

@@ -13,7 +13,6 @@ import { isClaudeCodeCompatible } from "../services/provider.ts";
import {
getAntigravityUserAgent,
GITHUB_COPILOT_CHAT_USER_AGENT,
getQwenOauthHeaders,
} from "./providerHeaderProfiles.ts";
import { normalizeCliCompatProviderId } from "@/shared/utils/cliCompat";
@@ -198,44 +197,6 @@ export const CLI_FINGERPRINTS: Record<string, CliFingerprint> = {
],
userAgent: getAntigravityUserAgent,
},
qwen: {
headerOrder: [
"Host",
"Content-Type",
"Authorization",
"User-Agent",
"X-Dashscope-AuthType",
"X-Dashscope-CacheControl",
"X-Dashscope-UserAgent",
"X-Stainless-Arch",
"X-Stainless-Lang",
"X-Stainless-Os",
"X-Stainless-Package-Version",
"X-Stainless-Retry-Count",
"X-Stainless-Runtime",
"X-Stainless-Runtime-Version",
"Connection",
"Accept",
"Accept-Language",
"Sec-Fetch-Mode",
"Accept-Encoding",
],
bodyFieldOrder: [
"model",
"messages",
"temperature",
"top_p",
"max_tokens",
"stream",
"tools",
"tool_choice",
"response_format",
"n",
"stop",
],
userAgent: getQwenOauthHeaders()["User-Agent"],
extraHeaders: getQwenOauthHeaders(),
},
};
/**

View File

@@ -76,10 +76,6 @@ export const OAUTH_ENDPOINTS = {
token: "https://api.anthropic.com/v1/oauth/token",
auth: "https://api.anthropic.com/v1/oauth/authorize",
},
qwen: {
token: "https://chat.qwen.ai/api/v1/oauth2/token", // From CLIProxyAPI
auth: "https://chat.qwen.ai/api/v1/oauth2/device/code", // From CLIProxyAPI
},
qoder: {
token: process.env.QODER_OAUTH_TOKEN_URL || "",
auth: process.env.QODER_OAUTH_AUTHORIZE_URL || "",

View File

@@ -13,11 +13,6 @@ export const GITHUB_COPILOT_USER_AGENT_LIBRARY = "electron-fetch";
export const QWEN_CLI_VERSION = "0.19.3";
export const QWEN_STAINLESS_LANG = "js";
export const QWEN_STAINLESS_PACKAGE_VERSION = "5.11.0";
export const QWEN_STAINLESS_RETRY_COUNT = "1";
export const QWEN_STAINLESS_RUNTIME = "node";
export const QWEN_ACCEPT_LANGUAGE = "*";
export const QWEN_SEC_FETCH_MODE = "cors";
export const QODER_DEFAULT_USER_AGENT = "Qoder-Cli";
@@ -85,8 +80,8 @@ export function normalizeStainlessArch(arch: string = getRuntimeArch()): string
}
export function getQwenCliUserAgent(version = QWEN_CLI_VERSION): string {
// Qwen Code builds this from the runtime process values. Keep it runtime-derived so
// packaged deployments use their own platform/architecture instead of a maintainer's host.
// Qoder's DashScope-compatible backend expects Qwen Code's runtime-derived wire identity.
// Keep it runtime-derived so packaged deployments use their own platform/architecture.
return `QwenCode/${version} (${getRuntimePlatform()}; ${getRuntimeArch()})`;
}
@@ -111,26 +106,6 @@ export function getGitHubCopilotRefreshHeaders(authorization: string): Record<st
};
}
export function getQwenOauthHeaders(): Record<string, string> {
const userAgent = getQwenCliUserAgent();
return {
"User-Agent": userAgent,
"X-Dashscope-AuthType": "qwen-oauth",
"X-Dashscope-CacheControl": "enable",
"X-Dashscope-UserAgent": userAgent,
"X-Stainless-Arch": normalizeStainlessArch(),
"X-Stainless-Lang": QWEN_STAINLESS_LANG,
"X-Stainless-Os": normalizeStainlessPlatform(),
"X-Stainless-Package-Version": QWEN_STAINLESS_PACKAGE_VERSION,
"X-Stainless-Retry-Count": QWEN_STAINLESS_RETRY_COUNT,
"X-Stainless-Runtime": QWEN_STAINLESS_RUNTIME,
"X-Stainless-Runtime-Version": getRuntimeVersion(),
Connection: "keep-alive",
"Accept-Language": QWEN_ACCEPT_LANGUAGE,
"Sec-Fetch-Mode": QWEN_SEC_FETCH_MODE,
};
}
export function getQoderDefaultHeaders(): Record<string, string> {
return {
"User-Agent": QODER_DEFAULT_USER_AGENT,

View File

@@ -83,7 +83,6 @@ import { snowflakeProvider } from "./registry/snowflake/index.ts";
import { huggingfaceProvider } from "./registry/huggingface/index.ts";
import { chipotleProvider } from "./registry/chipotle/index.ts";
import { freeaiapikeyProvider } from "./registry/freeaiapikey/index.ts";
import { qwenProvider } from "./registry/qwen/index.ts";
import { qwen_webProvider } from "./registry/qwen/web/index.ts";
import { zai_webProvider } from "./registry/zai-web/index.ts";
import { modalProvider } from "./registry/modal/index.ts";
@@ -284,7 +283,6 @@ export const REGISTRY: Record<string, RegistryEntry> = {
huggingface: huggingfaceProvider,
chipotle: chipotleProvider,
freeaiapikey: freeaiapikeyProvider,
qwen: qwenProvider,
"qwen-web": qwen_webProvider,
"zai-web": zai_webProvider,
modal: modalProvider,

View File

@@ -1,25 +0,0 @@
import type { RegistryEntry } from "../../shared.ts";
import { getQwenOauthHeaders, resolvePublicCred } from "../../shared.ts";
export const qwenProvider: RegistryEntry = {
id: "qwen",
alias: "qw",
format: "openai",
executor: "default",
baseUrl: "https://chat.qwen.ai/api/v1/services/aigc/text-generation/generation",
authType: "oauth",
authHeader: "bearer",
headers: getQwenOauthHeaders(),
oauth: {
clientIdEnv: "QWEN_OAUTH_CLIENT_ID",
clientIdDefault: resolvePublicCred("qwen_id"),
tokenUrl: "https://chat.qwen.ai/api/v1/oauth2/token",
authUrl: "https://chat.qwen.ai/api/v1/oauth2/device/code",
},
models: [
{ id: "qwen3-coder-plus", name: "Qwen3 Coder Plus" },
{ id: "qwen3-coder-flash", name: "Qwen3 Coder Flash" },
{ id: "vision-model", name: "Qwen3 Vision Model" },
{ id: "coder-model", name: "Qwen3.6 (Coder Model)" },
],
};

View File

@@ -2,8 +2,7 @@ import type { RegistryEntry } from "../../../shared.ts";
export const qwen_webProvider: RegistryEntry = {
id: "qwen-web",
// Distinct alias: the primary "qwen" provider keeps the short "qw" alias;
// this web/cookie variant is addressed by its own id.
// The web/cookie variant is addressed by its own id.
alias: "qwen-web",
format: "openai",
executor: "qwen-web",

View File

@@ -33,7 +33,6 @@ import {
getGitHubCopilotChatHeaders,
getKiroServiceHeaders,
getQoderDefaultHeaders,
getQwenOauthHeaders,
getRuntimePlatform,
getRuntimeArch,
} from "../providerHeaderProfiles.ts";
@@ -691,7 +690,6 @@ export {
getGitHubCopilotChatHeaders,
getKiroServiceHeaders,
getQoderDefaultHeaders,
getQwenOauthHeaders,
getRuntimePlatform,
getRuntimeArch,
resolvePublicCred,

View File

@@ -28,7 +28,6 @@ import {
getTargetFormat,
isClaudeCodeCompatible,
} from "../services/provider.ts";
import { sanitizeQwenThinkingToolChoice } from "../services/qwenThinking.ts";
import { getSapResourceGroup } from "../config/sap.ts";
import {
normalizeBailianMessagesUrl,
@@ -257,10 +256,6 @@ export class DefaultExecutor extends BaseExecutor {
return `${this.config.baseUrl}?beta=true`;
case "gemini":
return `${this.config.baseUrl}/${model}:${stream ? "streamGenerateContent?alt=sse" : "generateContent"}`;
case "qwen": {
const resourceUrl = credentials?.providerSpecificData?.resourceUrl;
return `https://${resourceUrl || "portal.qwen.ai"}/v1/chat/completions`;
}
default: {
// Honor a user-supplied custom base URL (providerSpecificData.baseUrl) for
// OpenAI-format providers (e.g. the built-in "openai" provider pointed at a
@@ -445,16 +440,6 @@ export class DefaultExecutor extends BaseExecutor {
headers["Accept"] = stream ? "text/event-stream" : "application/json";
// Qwen header cleanup: Remove X-Dashscope-* headers if using an API key (DashScope compatible mode).
// If using OAuth (Qwen Code), we MUST keep them for portal.qwen.ai to accept the request.
if (this.provider === "qwen" && effectiveKey) {
for (const key of Object.keys(headers)) {
if (key.toLowerCase().startsWith("x-dashscope-")) {
delete headers[key];
}
}
}
const isCompatibleProvider =
this.provider?.startsWith?.("openai-compatible-") ||
this.provider?.startsWith?.("anthropic-compatible-");
@@ -621,21 +606,11 @@ export class DefaultExecutor extends BaseExecutor {
withDefaults = withoutStreamOptions;
}
} else if (stream && targetFormat === "openai" && requestFormat !== "openai-responses") {
// Port of decolua/9router#663 (closes upstream #557): Qwen rejects with
// 400 "'stream_options' only set this when you set stream: true" when the
// outgoing body carries `stream: false` (Claude Code / Claude-Code-
// compatible callers force the executor-level stream flag on via
// `upstreamStream = stream || isClaudeCodeCompatible`, but the body keeps
// the caller's original `stream: false`). Same upstream also rejects the
// injection when `thinking` / `enable_thinking` is set. Skip injection in
// those cases instead of unconditionally adding `stream_options`.
// Do not inject stream_options when the outgoing body explicitly disables streaming.
const defaultsRecord = withDefaults as Record<string, unknown>;
const bodyDisablesStreamOptions =
defaultsRecord.stream !== undefined && defaultsRecord.stream !== true;
const qwenBlocksStreamOptions =
this.provider === "qwen" &&
(Boolean(defaultsRecord.thinking) || Boolean(defaultsRecord.enable_thinking));
if (bodyDisablesStreamOptions || qwenBlocksStreamOptions) {
if (bodyDisablesStreamOptions) {
if (Object.prototype.hasOwnProperty.call(defaultsRecord, "stream_options")) {
const withoutStreamOptions = { ...defaultsRecord };
delete withoutStreamOptions.stream_options;
@@ -695,15 +670,10 @@ export class DefaultExecutor extends BaseExecutor {
}
}
if (this.provider === "qwen" && typeof withDefaults === "object" && withDefaults !== null) {
return sanitizeQwenThinkingToolChoice(
withDefaults as Record<string, unknown>,
"QwenExecutor"
);
}
// Strip params unsupported by the target provider/model before sending upstream.
// Rules live in ../translator/paramSupport.ts (9router#7ae9fff6; fixes #1748).
// Config-driven strip of params unsupported by the target provider/model
// (e.g. claude-opus-4 deprecated `temperature` → Anthropic 400). Port from
// 9router#7ae9fff6 (fixes upstream #1748). Rules live in
// ../translator/paramSupport.ts so adding one means editing one table.
if (typeof withDefaults === "object" && withDefaults !== null) {
const bodyRecord = withDefaults as Record<string, unknown>;
const outboundModel = typeof bodyRecord.model === "string" ? bodyRecord.model : model;

View File

@@ -161,7 +161,6 @@ const executors = {
"doubao-web": new DoubaoWebExecutor(),
db: new DoubaoWebExecutor(), // Alias
"qwen-web": new QwenWebExecutor(),
qw: new QwenWebExecutor(), // Alias
"zai-web": new ZaiWebExecutor(),
zw: new ZaiWebExecutor(), // Alias
theoldllm: new TheOldLlmExecutor(),

View File

@@ -256,12 +256,6 @@ import {
} from "./chatCore/serviceTier.ts";
import { cacheReasoningFromAssistantMessage } from "../services/reasoningCache.ts";
import { sanitizeOpenAITool } from "../services/toolSchemaSanitizer.ts";
import {
setDetectedToolLimit,
parseToolLimitFromError,
shouldDetectLimit,
} from "../services/toolLimitDetector.ts";
import { isCompactResponsesEndpoint } from "../executors/codex.ts";
import { buildCodexQuotaPersistence } from "./chatCore/codexQuota.ts";
import { invalidateCodexQuotaCache } from "../services/codexQuotaFetcher.ts";
@@ -1744,9 +1738,7 @@ export async function handleChatCore({
finalCompressionBody?.messages ||
body?.contents ||
body?.request?.contents ||
(body?.input && typeof body.input === "object" && !Array.isArray(body.input)
? body.input
: []);
(body?.input && typeof body.input === "object" && !Array.isArray(body.input) ? body.input : []);
const finalEstimatedInputTokens =
estimateTokens(finalMessages) +
(Array.isArray(body?.tools) ? estimateTokens(body.tools) : 0) +
@@ -2479,7 +2471,7 @@ export async function handleChatCore({
const execute = async () => {
// Upstream body preparation extracted to chatCore/upstreamBody.ts (#3501 — first internal
// sub-slice of executeProviderRequest); produces the body sent upstream (payload rules +
// tool-limit truncation + qwen oauth user backfill + prompt_cache_key injection).
// tool-limit truncation + prompt_cache_key injection).
let bodyToSend = await prepareUpstreamBody({
translatedBody,
modelToCall,
@@ -2500,13 +2492,7 @@ export async function handleChatCore({
const rawResult = await (async () => {
let attempts = 0;
const isModelScopeForRequest = isModelScope();
const maxAttempts = isModelScopeForRequest
? 3
: provider === "qwen"
? 3
: provider === "codex"
? 3
: 1;
const maxAttempts = isModelScopeForRequest ? 3 : provider === "codex" ? 3 : 1;
// ── Codex 429 account-rotation state ─────────────────────────────────
// Track excluded connection IDs for codex failover across attempts.
@@ -2611,26 +2597,6 @@ export async function handleChatCore({
recordKeyHealthStatus(401, execCreds);
}
// Qwen 429 strict quota backoff (wait 1.5s, 3s and retry)
if (
provider === "qwen" &&
res.response.status === 429 &&
attempts < maxAttempts - 1
) {
const bodyPeek = await res.response
.clone()
.text()
.catch(() => "");
if (bodyPeek.toLowerCase().includes("exceeded your current quota")) {
const delay = 1500 * (attempts + 1);
log?.warn?.("QWEN_RETRY", `Quota 429 hit. Retrying in ${delay}ms...`);
releaseAccountSemaphore();
await new Promise((r) => setTimeout(r, delay));
attempts++;
continue;
}
}
if (isModelScope() && res.response.status === 429 && attempts < maxAttempts - 1) {
const bodyPeek = await res.response
.clone()
@@ -3196,39 +3162,12 @@ export async function handleChatCore({
upstreamErrorType
);
}
// We need to peek at the error text if it's 400 for Qwen
let upstreamErrorParsed = false;
let parsedStatusCode = providerResponse.status;
let parsedMessage = "";
let parsedRetryAfterMs: number | null = null;
let upstreamErrorBody: unknown = null;
if (provider === "qwen" && providerResponse.status === HTTP_STATUS.BAD_REQUEST) {
const errorDetails = await parseUpstreamError(providerResponse, provider);
parsedStatusCode = errorDetails.statusCode;
parsedMessage = errorDetails.message;
parsedRetryAfterMs = errorDetails.retryAfterMs;
upstreamErrorBody = errorDetails.responseBody;
upstreamErrorParsed = true;
}
const errorMessageForToolDetection =
typeof upstreamErrorBody === "string"
? upstreamErrorBody
: JSON.stringify(upstreamErrorBody ?? {});
if (shouldDetectLimit(errorMessageForToolDetection, parsedStatusCode)) {
const detectedLimit = parseToolLimitFromError(errorMessageForToolDetection);
if (detectedLimit) {
setDetectedToolLimit(provider, detectedLimit);
log?.info?.("TOOL_LIMIT", `Detected tool limit ${detectedLimit} for ${provider}`);
}
}
const isQwenExpiredError =
provider === "qwen" &&
parsedStatusCode === HTTP_STATUS.BAD_REQUEST &&
parsedMessage?.toLowerCase().includes("session has expired");
// Track whether stream_options was present and stripped — if so, 401/403 after
// that may be from the modification rather than a genuine auth failure, so we
// skip the credential refresh attempt in that case.
@@ -3238,11 +3177,10 @@ export async function handleChatCore({
delete translatedBody.stream_options;
}
// Handle 401/403 (and Qwen explicit expiration) - try token refresh using executor
// Handle 401/403 - try token refresh using executor
if (
(providerResponse.status === HTTP_STATUS.UNAUTHORIZED ||
providerResponse.status === HTTP_STATUS.FORBIDDEN ||
isQwenExpiredError) &&
providerResponse.status === HTTP_STATUS.FORBIDDEN) &&
!hadStreamOptions // Skip refresh if failure may be from stream_options removal, not auth
) {
// Fix A: wrap refreshCredentials in runWithOnPersist so the persist callback

View File

@@ -4,8 +4,8 @@
*
* Extracted from handleChatCore's execute() closure: prepares the body actually sent upstream for a
* given target model. Pins the model id, applies the configured payload rules, truncates the tool
* list to the provider's effective limit, backfills a default `user` for Qwen OAuth requests, and
* injects an OpenAI `prompt_cache_key` for caching-capable providers. Pure with respect to handler
* list to the provider's effective limit and injects an OpenAI `prompt_cache_key` for
* caching-capable providers. Pure with respect to handler
* state (returns a fresh body, only logs as a side effect); behaviour is byte-identical to the
* previous inline block. Split into small private steps so each stays under the complexity cap.
*/
@@ -26,7 +26,11 @@ import { sanitizeRequestForResolvedTarget } from "../../services/targetRequestSa
type LoggerLike = { debug?: (...args: unknown[]) => void } | null | undefined;
type Body = Record<string, unknown>;
type CredentialsLike =
| { apiKey?: unknown; accessToken?: unknown; providerSpecificData?: Record<string, unknown> | null }
| {
apiKey?: unknown;
accessToken?: unknown;
providerSpecificData?: Record<string, unknown> | null;
}
| null
| undefined;
@@ -83,27 +87,6 @@ function truncateToolList(
return bodyToSend;
}
// Qwen OAuth rejects requests without a non-empty `user` field. Some minimal OpenAI-compatible
// clients omit it, so we backfill a stable default only for OAuth mode (API key mode is unaffected).
function backfillQwenOAuthUser(
bodyToSend: Body,
provider: string | null | undefined,
credentials: CredentialsLike,
log?: LoggerLike
): Body {
const hasValidQwenUser = typeof bodyToSend.user === "string" && bodyToSend.user.trim().length > 0;
const isQwenOAuthRequest =
provider === "qwen" &&
!credentials?.apiKey &&
typeof credentials?.accessToken === "string" &&
credentials.accessToken.trim().length > 0;
if (isQwenOAuthRequest && !hasValidQwenUser) {
bodyToSend = { ...bodyToSend, user: "omniroute-qwen-oauth" };
log?.debug?.("QWEN", "Injected fallback user for OAuth request");
}
return bodyToSend;
}
// Inject prompt_cache_key only for providers that support it.
async function injectPromptCacheKey(
bodyToSend: Body,
@@ -175,9 +158,13 @@ export async function prepareUpstreamBody(opts: {
log,
});
bodyToSend = truncateToolList(bodyToSend, provider, bypassDefaultToolLimit ?? false, log);
bodyToSend = backfillQwenOAuthUser(bodyToSend, provider, credentials, log);
const connectionCacheOverride = resolveConnectionCacheOverride(credentials?.providerSpecificData);
bodyToSend = await injectPromptCacheKey(bodyToSend, provider, targetFormat, connectionCacheOverride);
bodyToSend = await injectPromptCacheKey(
bodyToSend,
provider,
targetFormat,
connectionCacheOverride
);
return bodyToSend;
}

View File

@@ -60,7 +60,6 @@ export {
refreshAccessToken,
refreshClaudeOAuthToken,
refreshGoogleToken,
refreshQwenToken,
refreshCodexToken,
refreshQoderToken,
refreshGitHubToken,

View File

@@ -49,12 +49,6 @@ describe("TierResolver", () => {
assert.equal(result.hasFreeTier, true);
});
it("classifies Qwen as free", () => {
const result = classifyTier("qwen", "qwen3-coder-plus");
assert.equal(result.tier, PROVIDER_TIER.FREE);
assert.equal(result.hasFreeTier, true);
});
it("classifies Cloudflare AI as free", () => {
const result = classifyTier("cloudflare-ai", "llama-3.3-70b");
assert.equal(result.tier, PROVIDER_TIER.FREE);
@@ -188,15 +182,14 @@ describe("TierResolver", () => {
{ provider: "anthropic", model: "claude-opus-4-7" },
{ provider: "groq", model: "llama-3.3-70b" },
{ provider: "qoder", model: "kimi-k2-thinking" },
{ provider: "qwen", model: "qwen3-coder-plus" },
{ provider: "unknown", model: "unknown-model" },
];
const results = classifyTiers(targets);
assert.equal(results.length, 10);
assert.equal(results.length, 9);
assert.equal(results[0].tier, PROVIDER_TIER.FREE); // kiro
assert.equal(results[1].tier, PROVIDER_TIER.PREMIUM); // openai gpt-4o ($2.50/M)
assert.equal(results[2].tier, PROVIDER_TIER.CHEAP); // deepseek
assert.equal(results[9].tier, PROVIDER_TIER.PREMIUM); // unknown
assert.equal(results[8].tier, PROVIDER_TIER.PREMIUM); // unknown
});
it("uses cache for repeated models", () => {
@@ -230,7 +223,6 @@ describe("TierResolver", () => {
"pollinations",
"longcat",
"cloudflare-ai",
"qwen",
"nvidia-nim",
"cerebras",
"groq",

View File

@@ -27,7 +27,6 @@ const ROTATION_LOCK_GROUP: Record<string, string> = {
"gitlab-duo": "gitlab-duo",
kiro: "kiro",
"kimi-coding": "kimi-coding",
qwen: "qwen",
};
// Protective settle gap (ms) between two consecutive sibling refreshes when the

View File

@@ -51,7 +51,6 @@ export const LEGACY_FREE_PROVIDERS: readonly string[] = [
"pollinations",
"longcat",
"cloudflare-ai",
"qwen",
"nvidia-nim",
"cerebras",
"groq",

View File

@@ -18,7 +18,6 @@
"pollinations",
"longcat",
"cloudflare-ai",
"qwen",
"nvidia-nim",
"cerebras",
"groq"

View File

@@ -23,7 +23,6 @@ import { refreshKimiCodingToken } from "./tokenRefresh/providers/kimiCoding.ts";
import { refreshGitLabDuoToken } from "./tokenRefresh/providers/gitlabDuo.ts";
import { refreshClaudeOAuthToken } from "./tokenRefresh/providers/claudeOAuth.ts";
import { refreshGoogleToken } from "./tokenRefresh/providers/google.ts";
import { refreshQwenToken } from "./tokenRefresh/providers/qwen.ts";
import { refreshCodexToken } from "./tokenRefresh/providers/codex.ts";
import { refreshKiroToken } from "./tokenRefresh/providers/kiro.ts";
import { refreshQoderToken } from "./tokenRefresh/providers/qoder.ts";
@@ -38,7 +37,6 @@ export {
refreshGitLabDuoToken,
refreshClaudeOAuthToken,
refreshGoogleToken,
refreshQwenToken,
refreshCodexToken,
refreshKiroToken,
refreshQoderToken,
@@ -77,7 +75,6 @@ export const REFRESH_LEAD_MS: Record<string, number> = {
"gitlab-duo": 5 * 60 * 1000, // GitLab token family revocation on misuse
kiro: 5 * 60 * 1000, // AWS SSO OIDC issues one-time-use refresh tokens
"kimi-coding": 5 * 60 * 1000, // Moonshot rotates per-refresh
qwen: 5 * 60 * 1000, // Alibaba device-code path also rotates
// Non-rotating providers — longer lead is safe.
iflow: 24 * 60 * 60 * 1000, // 24 hours
// Google OAuth refresh_tokens are permanent (non-rotating) — longer lead
@@ -391,9 +388,6 @@ async function _getAccessTokenInternal(provider, credentials, log, proxyConfig:
case "codex":
return await refreshCodexToken(credentials.refreshToken, log, proxyConfig);
case "qwen":
return await refreshQwenToken(credentials.refreshToken, log, proxyConfig);
case "qoder":
return await refreshQoderToken(credentials.refreshToken, log, proxyConfig);
@@ -457,7 +451,6 @@ export function supportsTokenRefresh(provider) {
"agy",
"claude",
"codex",
"qwen",
"qoder",
"github",
"kiro",
@@ -767,7 +760,6 @@ export function formatProviderCredentials(provider, credentials, log) {
};
case "codex":
case "qwen":
case "qoder":
case "openai":
case "openrouter":

View File

@@ -1,81 +0,0 @@
// @ts-nocheck
// Extracted from open-sse/services/tokenRefresh.ts — see ../shared.ts for
// provenance notes (ported idea from KooshaPari's PR #7338, redone on tip).
import { PROVIDERS, OAUTH_ENDPOINTS } from "../../../config/constants.ts";
import { runWithProxyContext } from "../../../utils/proxyFetch.ts";
import { buildFormParams } from "../shared.ts";
export async function refreshQwenToken(refreshToken, log, proxyConfig: unknown = null) {
const endpoint = OAUTH_ENDPOINTS.qwen.token;
try {
const response = await runWithProxyContext(proxyConfig, () =>
fetch(endpoint, {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
Accept: "application/json",
},
body: buildFormParams({
grant_type: "refresh_token",
refresh_token: refreshToken,
client_id: PROVIDERS.qwen.clientId,
}),
})
);
if (response.status === 200) {
const tokens = await response.json();
log?.info?.("TOKEN_REFRESH", "Successfully refreshed Qwen token", {
hasNewAccessToken: !!tokens.access_token,
hasNewRefreshToken: !!tokens.refresh_token,
expiresIn: tokens.expires_in,
});
return {
accessToken: tokens.access_token,
refreshToken: tokens.refresh_token || refreshToken,
expiresIn: tokens.expires_in,
providerSpecificData: tokens.resource_url
? { resourceUrl: tokens.resource_url }
: undefined,
};
} else {
const errorText = await response.text().catch(() => "");
// Detect unrecoverable invalid_request (expired/revoked refresh token or bad client_id)
let errorCode = null;
try {
const parsed = JSON.parse(errorText);
errorCode = parsed?.error;
} catch {
// not JSON, ignore
}
if (errorCode === "invalid_request" || errorCode === "invalid_grant") {
log?.error?.(
"TOKEN_REFRESH",
"Qwen refresh token is invalid or expired. Re-authentication required.",
{
status: response.status,
errorCode,
}
);
return { error: "unrecoverable_refresh_error", code: errorCode };
}
log?.warn?.("TOKEN_REFRESH", `Error with Qwen endpoint`, {
status: response.status,
error: errorText,
});
}
} catch (error) {
log?.warn?.("TOKEN_REFRESH", `Network error trying Qwen endpoint`, {
error: error.message,
});
}
log?.error?.("TOKEN_REFRESH", "Failed to refresh Qwen token");
return null;
}

View File

@@ -519,7 +519,6 @@ export const USAGE_FETCHER_PROVIDERS = [
"amazon-q",
"kimi-coding",
"kimi-coding-apikey",
"qwen",
"qoder",
"glm",
"glm-cn",
@@ -584,8 +583,6 @@ export async function getUsageForProvider(
case "kimi-coding":
case "kimi-coding-apikey":
return await getKimiUsage(accessToken, apiKey, providerSpecificData);
case "qwen":
return await getQwenUsage(accessToken, providerSpecificData);
case "qoder":
// Qoder PATs live in `apiKey` (decrypted) or `providerSpecificData.qoderPat`,
// never in `accessToken`.
@@ -885,24 +882,6 @@ async function getVertexUsage(connectionId: string, provider: string) {
}
}
/**
* Qwen Usage
*/
async function getQwenUsage(accessToken?: string, providerSpecificData?: JsonRecord) {
void accessToken;
try {
const resourceUrl = providerSpecificData?.resourceUrl;
if (!resourceUrl) {
return { message: "Qwen connected. No resource URL available." };
}
// Qwen may have usage endpoint at resource URL
return { message: "Qwen connected. Usage tracked per request." };
} catch (error) {
return { message: "Unable to fetch Qwen usage." };
}
}
/**
* Qoder Usage
*

View File

@@ -7,7 +7,7 @@
* Client-side caching (e.g., Claude Code) should be preserved when:
* 1. Client is Claude Code or similar caching-aware client
* 2. Request will hit a deterministic target (single model or deterministic combo strategy)
* 3. Provider supports prompt caching (Anthropic, Alibaba Qwen, etc.)
* 3. Provider supports prompt caching (Anthropic, etc.)
*/
import type { RoutingStrategyValue } from "../../src/shared/constants/routingStrategies";
@@ -76,7 +76,6 @@ const CACHING_PROVIDERS = new Set([
"claude",
"anthropic",
"zai",
"qwen",
"deepseek",
// Kimi Code's OpenAI protocol requires prompt_cache_key for Coding Plan
// cache affinity. The OAuth card and hidden API-key compatibility ID share

View File

@@ -165,11 +165,6 @@ const EMBEDDED_DEFAULTS = {
14, 29, 30, 54, 55, 34, 26, 21, 8, 104, 53, 47, 85, 95, 15, 83, 110, 29, 105, 14, 53, 30, 94,
26, 29, 20, 26, 11,
],
// Qwen Code CLI — qwen oauth client (public, device flow)
qwen_id: [
9, 93, 93, 89, 70, 92, 66, 71, 7, 26, 68, 20, 86, 88, 13, 81, 79, 67, 9, 91, 12, 93, 15, 16, 88,
69, 23, 4, 20, 21, 64, 84,
],
// Kimi coding CLI — moonshot oauth client (public)
kimi_id: [
94, 90, 11, 92, 20, 89, 66, 69, 72, 73, 65, 76, 86, 65, 93, 7, 75, 20, 28, 86, 90, 94, 95, 95,

View File

@@ -52,6 +52,7 @@ export const SPAWN_CAPABLE_ROUTE_ROOTS: ReadonlyArray<string> = [
"src/app/api/skills/collect", // Skill Collector CLI detection: GET .../detect spawns a child process per CLI_TOOL_IDS entry via getCliRuntimeStatus() (Hard Rules #15 + #17, PR #6294 review)
"src/app/api/cli-tools/forge-settings", // GET calls getCliRuntimeStatus() to detect the `forge` CLI install (Hard Rules #15 + #17, #7263)
"src/app/api/cli-tools/jcode-settings", // GET calls getCliRuntimeStatus() to detect the `jcode` CLI install (Hard Rules #15 + #17, #7263)
"src/app/api/cli-tools/qwen-settings", // GET calls getCliRuntimeStatus("qwen") and writes local ~/.qwen config files (Hard Rules #15 + #17)
];
// Frozen pre-existing exceptions: spawn-capable routes NOT yet classified

View File

@@ -48,6 +48,7 @@ export default function DefaultToolCard({
);
const isMultiModelTool = tool.modelSelectionMode === "multiple";
const usesOpenCodePreview = tool.previewConfigMode === "opencode";
const usesQwenCodePreview = tool.previewConfigMode === "qwen";
const selectedKeyObj = apiKeys?.find((k) => k.id === selectedApiKeyId);
const resolveApiKeyValue = useCallback(
@@ -187,15 +188,18 @@ export default function DefaultToolCard({
}, [isExpanded, runtimeStatus, t, toolId]);
const replaceVars = useCallback(
(text) => {
(text, modelOverride = "") => {
const keyToUse = resolveApiKeyValue();
return text
.replace(/\{\{baseUrl\}\}/g, baseUrlWithV1)
.replace(/\{\{apiKey\}\}/g, keyToUse)
.replace(/\{\{model\}\}/g, getSelectedModelLabels()[0] || t("modelPlaceholder"));
.replace(
/\{\{model\}\}/g,
modelOverride || getSelectedModelLabels()[0] || t("modelPlaceholder")
);
},
[baseUrl, getSelectedModelLabels, resolveApiKeyValue, t]
[baseUrlWithV1, getSelectedModelLabels, resolveApiKeyValue, t]
);
const handleCopy = async (text, field) => {
@@ -211,6 +215,9 @@ export default function DefaultToolCard({
const getRenderedCodeBlock = useCallback(() => {
if (!tool.codeBlock?.code) return "";
if (usesQwenCodePreview) {
return replaceVars(tool.codeBlock.code, getSelectedModels()[0]);
}
if (!usesOpenCodePreview) return replaceVars(tool.codeBlock.code);
const keyToUse = resolveApiKeyValue();
@@ -226,13 +233,14 @@ export default function DefaultToolCard({
2
);
}, [
baseUrl,
baseUrlWithV1,
getSelectedModels,
getSelectedModelLabelMap,
replaceVars,
resolveApiKeyValue,
tool.codeBlock?.code,
usesOpenCodePreview,
usesQwenCodePreview,
]);
const handleSelectModel = (model) => {
@@ -265,7 +273,11 @@ export default function DefaultToolCard({
// (#523) Prefer keyId lookup so the backend writes the real key to disk.
const selectedKeyId = selectedApiKeyId?.trim() || null;
const res = await fetch(`/api/cli-tools/guide-settings/${toolId}`, {
const saveEndpoint =
toolId === "qwen"
? "/api/cli-tools/qwen-settings"
: `/api/cli-tools/guide-settings/${toolId}`;
const res = await fetch(saveEndpoint, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({

View File

@@ -407,7 +407,7 @@ const COMBO_TEMPLATE_FALLBACK = {
balancedDesc: "Least-used routing to spread demand over time.",
freeStackTitle: "Free Stack ($0)",
freeStackDesc:
"Round-robin across free providers: Kiro, Qoder, Qwen, Antigravity CLI. Zero cost, never stops.",
"Round-robin across free providers: Kiro, Qoder, Antigravity CLI. Zero cost, never stops.",
paidPremiumTitle: "Paid Premium",
paidPremiumDesc:
"Round-robin across paid subscriptions: Cursor, Antigravity. Top-tier models, distributed load.",

View File

@@ -40,7 +40,6 @@ export const SUPPORTED_WIZARD_OAUTH_PROVIDER_IDS = new Set([
"codex",
"antigravity",
"agy",
"qwen",
"kimi-coding",
"github",
"gitlab-duo",

View File

@@ -11,6 +11,7 @@ import { CLI_TOOLS } from "@/shared/constants/cliTools";
import { getCliRuntimeStatus, getCliPrimaryConfigPath } from "@/shared/services/cliRuntime";
import { getAllCliToolLastConfigured } from "@/lib/db/cliToolState";
import { checkToolConfigStatus } from "@/lib/cliTools/checkToolConfigStatus";
import { findOmniRouteQwenCodeModel } from "@/shared/services/qwenCodeConfig";
import { getCached, setCached } from "@/lib/cliTools/batchStatusCache";
import type { ToolBatchStatus, ToolBatchStatusMap } from "@/shared/types/cliBatchStatus";
@@ -43,13 +44,8 @@ async function extractEndpointFromConfig(
return (env?.ANTHROPIC_BASE_URL as string | undefined) ?? null;
}
case "qwen": {
const mp = config.modelProviders as Record<string, unknown>[] | undefined;
if (!Array.isArray(mp)) return null;
for (const provider of mp) {
const baseUrl = (provider as Record<string, unknown>).apiBase as string | undefined;
if (baseUrl) return baseUrl;
}
return null;
const managed = findOmniRouteQwenCodeModel(config);
return typeof managed?.baseUrl === "string" ? managed.baseUrl : null;
}
case "cline":
return (config.openAiBaseUrl as string | undefined) ?? null;

View File

@@ -66,8 +66,6 @@ export async function POST(request, { params }) {
// (#524) OpenCode config was never saved because only 'continue' was handled here.
// OpenCode reads ~/.config/opencode/opencode.json — write the OmniRoute settings there.
return await saveOpenCodeConfig({ baseUrl, apiKey, model, models, modelLabels });
case "qwen":
return await saveQwenConfig({ baseUrl, apiKey, model });
case "hermes":
return await saveHermesConfig({ baseUrl, apiKey, model });
// hermes-agent now uses the dedicated /api/cli-tools/hermes-agent-settings endpoint
@@ -205,60 +203,6 @@ async function saveOpenCodeConfig({ baseUrl, apiKey, model, models, modelLabels
});
}
/**
* Save Qwen Code config to ~/.qwen/settings.json
*
* Uses security.auth format (not modelProviders) since Qwen Code
* prioritizes security.auth.selectedType over modelProviders entries.
* Per official docs: security.auth takes highest precedence.
*/
async function saveQwenConfig({ baseUrl, apiKey, model }) {
const home = os.homedir();
const configPath = path.join(home, ".qwen", "settings.json");
await fs.mkdir(path.dirname(configPath), { recursive: true });
const normalizedBaseUrl = String(baseUrl || "")
.trim()
.replace(/\/+$/, "");
const resolvedApiKey = apiKey || "sk_omniroute";
const resolvedModel = model || "qwen/qwen3-coder-plus";
// Read existing config to preserve other settings (permissions, mcpServers, etc.)
let existingConfig: Record<string, any> = {};
try {
const raw = await fs.readFile(configPath, "utf-8");
existingConfig = JSON.parse(raw);
} catch {
// File doesn't exist or invalid JSON
}
// Set security.auth for openai auth type with direct credentials
// This takes priority over modelProviders entries (per Qwen docs)
existingConfig.security = {
...existingConfig.security,
auth: {
selectedType: "openai",
apiKey: resolvedApiKey,
baseUrl: normalizedBaseUrl,
},
};
// Set model to the selected model
existingConfig.model = {
...existingConfig.model,
name: resolvedModel,
};
await fs.writeFile(configPath, JSON.stringify(existingConfig, null, 2), "utf-8");
return NextResponse.json({
success: true,
message: `Qwen Code config saved to ${configPath}`,
configPath,
});
}
/**
* Save Hermes config to ~/.hermes/config.yaml
*

View File

@@ -1,119 +1,112 @@
"use server";
import fs from "node:fs/promises";
import path from "node:path";
import pino from "pino";
import { NextResponse } from "next/server";
import fs from "fs/promises";
import path from "path";
import os from "os";
import { sanitizeErrorMessage } from "@omniroute/open-sse/utils/error";
import { requireCliToolsAuth } from "@/lib/api/requireCliToolsAuth";
import { getApiKeyById } from "@/lib/db/apiKeys";
import { deleteCliToolLastConfigured, saveCliToolLastConfigured } from "@/lib/db/cliToolState";
import { createMultiBackup } from "@/shared/services/backupService";
import {
hasOmniRouteQwenCodeConfig,
mergeQwenCodeEnv,
mergeQwenCodeSettings,
removeQwenCodeEnv,
removeQwenCodeSettings,
} from "@/shared/services/qwenCodeConfig";
import {
ensureCliConfigWriteAllowed,
getCliPrimaryConfigPath,
getCliConfigPaths,
getCliRuntimeStatus,
} from "@/shared/services/cliRuntime";
import { createBackup } from "@/shared/services/backupService";
import { saveCliToolLastConfigured, deleteCliToolLastConfigured } from "@/lib/db/cliToolState";
import { cliModelConfigSchema } from "@/shared/validation/schemas";
import { isValidationFailure, validateBody } from "@/shared/validation/helpers";
import { getApiKeyById } from "@/lib/localDb";
import { cliModelConfigSchema } from "@/shared/validation/schemas";
const getQwenSettingsPath = () => getCliPrimaryConfigPath("qwen");
const getQwenDir = () => path.dirname(getQwenSettingsPath());
const getQwenEnvPath = () => path.join(getQwenDir(), ".env");
const logger = pino({ name: "qwen-code-settings-api" });
// Read current settings.json
const readSettings = async () => {
const getPaths = (): { settings: string; env: string } => {
const paths = getCliConfigPaths("qwen");
if (!paths?.settings || !paths.env) throw new Error("Qwen Code config paths are unavailable");
return { settings: paths.settings, env: paths.env };
};
const readTextIfPresent = async (filePath: string): Promise<string> => {
try {
const settingsPath = getQwenSettingsPath();
const content = await fs.readFile(settingsPath, "utf-8");
return JSON.parse(content);
} catch (error: any) {
if (error.code === "ENOENT") return null;
return await fs.readFile(filePath, "utf8");
} catch (error) {
if ((error as NodeJS.ErrnoException).code === "ENOENT") return "";
throw error;
}
};
// Read current .env file
const readEnv = async () => {
const fileExists = async (filePath: string): Promise<boolean> => {
try {
const envPath = getQwenEnvPath();
return await fs.readFile(envPath, "utf-8");
} catch (error: any) {
if (error.code === "ENOENT") return "";
await fs.access(filePath);
return true;
} catch {
return false;
}
};
const readSettings = async (filePath: string): Promise<Record<string, unknown>> => {
const text = await readTextIfPresent(filePath);
if (!text.trim()) return {};
const parsed: unknown = JSON.parse(text);
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
throw new Error("Qwen Code settings.json must contain a JSON object");
}
return parsed as Record<string, unknown>;
};
const writeAtomic = async (filePath: string, content: string, mode?: number): Promise<void> => {
const tempPath = `${filePath}.${process.pid}.${Date.now()}.tmp`;
try {
await fs.writeFile(tempPath, content, { encoding: "utf8", mode });
if (mode !== undefined) await fs.chmod(tempPath, mode);
await fs.rename(tempPath, filePath);
} catch (error) {
await fs.unlink(tempPath).catch(() => {});
throw error;
}
};
// Check if settings has OmniRoute config
const hasOmniRouteConfig = (settings: any) => {
if (!settings || !settings.modelProviders) return false;
const openai = settings.modelProviders.openai;
if (!Array.isArray(openai)) return false;
return openai.some((p: any) => {
if (p.name?.includes("OmniRoute") || p.id === "omniroute") return true;
if (!p.baseUrl) return false;
try {
const urlObj = new URL(p.baseUrl);
const host = urlObj.hostname;
const isDashScope =
host === "dashscope.aliyuncs.com" || host.endsWith(".dashscope.aliyuncs.com");
const isOpenAI = host === "api.openai.com" || host.endsWith(".openai.com");
return !isDashScope && !isOpenAI;
} catch {
return true; // invalid URLs are treated as custom endpoints
}
});
};
// GET - Check Qwen CLI and read current settings
export async function GET(request: Request) {
export async function GET(request: Request): Promise<Response> {
const authError = await requireCliToolsAuth(request);
if (authError) return authError;
try {
const runtime = await getCliRuntimeStatus("qwen");
if (!runtime.installed || !runtime.runnable) {
return NextResponse.json({
installed: runtime.installed,
runnable: runtime.runnable,
command: runtime.command,
commandPath: runtime.commandPath,
runtimeMode: runtime.runtimeMode,
reason: runtime.reason,
settings: null,
message:
runtime.installed && !runtime.runnable
? "Qwen Code CLI is installed but not runnable"
: "Qwen Code CLI is not installed",
});
}
const settings = await readSettings();
const configPaths = getPaths();
const [runtime, settings] = await Promise.all([
getCliRuntimeStatus("qwen"),
readSettings(configPaths.settings),
]);
return NextResponse.json({
installed: runtime.installed,
runnable: runtime.runnable,
command: runtime.command,
commandPath: runtime.commandPath,
runtimeMode: runtime.runtimeMode,
reason: runtime.reason,
...runtime,
settings,
hasOmniRoute: hasOmniRouteConfig(settings),
settingsPath: getQwenSettingsPath(),
envPath: getQwenEnvPath(),
hasOmniRoute: hasOmniRouteQwenCodeConfig(settings),
settingsPath: configPaths.settings,
envPath: configPaths.env,
});
} catch (error) {
console.log("Error checking qwen settings:", error);
return NextResponse.json({ error: "Failed to check qwen settings" }, { status: 500 });
logger.error({ err: error }, "Failed to read Qwen Code settings");
return NextResponse.json(
{ error: sanitizeErrorMessage(error instanceof Error ? error.message : String(error)) },
{ status: 500 }
);
}
}
// POST - Write OmniRoute config to settings.json + .env
export async function POST(request: Request) {
export async function POST(request: Request): Promise<Response> {
const authError = await requireCliToolsAuth(request);
if (authError) return authError;
let rawBody;
let rawBody: unknown;
try {
rawBody = await request.json();
} catch {
@@ -128,228 +121,91 @@ export async function POST(request: Request) {
);
}
const validation = validateBody(cliModelConfigSchema, rawBody);
if (isValidationFailure(validation)) {
return NextResponse.json({ error: validation.error }, { status: 400 });
}
const writeGuard = ensureCliConfigWriteAllowed();
if (writeGuard) return NextResponse.json({ error: writeGuard }, { status: 403 });
try {
const writeGuard = ensureCliConfigWriteAllowed();
if (writeGuard) {
return NextResponse.json({ error: writeGuard }, { status: 403 });
}
// Extract keyId BEFORE validation — Zod strips unknown fields
const keyId = typeof rawBody?.keyId === "string" ? rawBody.keyId.trim() : null;
const validation = validateBody(cliModelConfigSchema, rawBody);
if (isValidationFailure(validation)) {
return NextResponse.json({ error: validation.error }, { status: 400 });
}
let { baseUrl, apiKey, model } = validation.data;
// Resolve real key from DB by ID
const configPaths = getPaths();
const bodyRecord = rawBody as Record<string, unknown>;
const keyId = typeof bodyRecord.keyId === "string" ? bodyRecord.keyId.trim() : "";
let apiKey = validation.data.apiKey || "";
if (keyId) {
try {
const keyRecord = await getApiKeyById(keyId);
if (keyRecord?.key) apiKey = keyRecord.key as string;
} catch {
/* non-critical */
}
const keyRecord = await getApiKeyById(keyId);
if (keyRecord?.key) apiKey = keyRecord.key;
}
if (!apiKey) apiKey = "sk_omniroute";
const resolvedApiKey = apiKey || "sk_omniroute";
const resolvedModel = model || "coder-model";
const normalizedBaseUrl = String(baseUrl || "")
.trim()
.replace(/\/+$/, "");
const qwenDir = getQwenDir();
const settingsPath = getQwenSettingsPath();
const envPath = getQwenEnvPath();
// Ensure directory exists
await fs.mkdir(qwenDir, { recursive: true });
// Backup current settings before modifying
await createBackup("qwen", settingsPath);
// --- Write API keys to ~/.qwen/.env ---
let envContent = await readEnv();
const envLines = envContent.split("\n").filter((line) => {
// Remove old OmniRoute-related keys we're about to write
return (
!line.startsWith("OPENAI_API_KEY=") &&
!line.startsWith("ANTHROPIC_API_KEY=") &&
!line.startsWith("GEMINI_API_KEY=")
);
const [existingSettings, existingEnv] = await Promise.all([
readSettings(configPaths.settings),
readTextIfPresent(configPaths.env),
]);
const nextSettings = mergeQwenCodeSettings(existingSettings, {
baseUrl: validation.data.baseUrl,
model: validation.data.model,
});
const nextEnv = mergeQwenCodeEnv(existingEnv, apiKey);
envLines.push(`OPENAI_API_KEY=${resolvedApiKey}`);
envLines.push(`ANTHROPIC_API_KEY=${resolvedApiKey}`);
envLines.push(`GEMINI_API_KEY=${resolvedApiKey}`);
await fs.mkdir(path.dirname(configPaths.settings), { recursive: true, mode: 0o700 });
await createMultiBackup("qwen", [configPaths.settings, configPaths.env]);
await writeAtomic(configPaths.settings, `${JSON.stringify(nextSettings, null, 2)}\n`);
await writeAtomic(configPaths.env, nextEnv, 0o600);
await fs.writeFile(envPath, envLines.join("\n").trim() + "\n", "utf-8");
// --- Write modelProviders to settings.json ---
let existingConfig: Record<string, any> = {};
try {
const raw = await fs.readFile(settingsPath, "utf-8");
existingConfig = JSON.parse(raw);
} catch {
// File doesn't exist or invalid JSON
}
if (!existingConfig.modelProviders) existingConfig.modelProviders = {};
// openai provider — primary, supports all models via OmniRoute
const openaiEntry = {
id: resolvedModel,
name: `${resolvedModel} (OmniRoute)`,
envKey: "OPENAI_API_KEY",
baseUrl: normalizedBaseUrl,
generationConfig: {
contextWindowSize: 200000,
},
};
if (!existingConfig.modelProviders.openai) existingConfig.modelProviders.openai = [];
const openaiProviders = existingConfig.modelProviders.openai;
const openaiIdx = openaiProviders.findIndex(
(p: any) => p && (p.baseUrl === normalizedBaseUrl || p.id === "omniroute")
);
if (openaiIdx >= 0) {
openaiProviders[openaiIdx] = openaiEntry;
} else {
openaiProviders.push(openaiEntry);
}
// anthropic provider — for Claude models via OmniRoute
const anthropicEntry = {
id: "claude-sonnet-4-6",
name: "Claude Sonnet 4.6 (OmniRoute)",
envKey: "ANTHROPIC_API_KEY",
baseUrl: normalizedBaseUrl,
generationConfig: {
contextWindowSize: 200000,
},
};
if (!existingConfig.modelProviders.anthropic) existingConfig.modelProviders.anthropic = [];
const anthropicProviders = existingConfig.modelProviders.anthropic;
const anthropicIdx = anthropicProviders.findIndex(
(p: any) => p && p.baseUrl === normalizedBaseUrl
);
if (anthropicIdx >= 0) {
anthropicProviders[anthropicIdx] = anthropicEntry;
} else {
anthropicProviders.push(anthropicEntry);
}
// gemini provider — for Gemini models via OmniRoute
const geminiEntry = {
id: "gemini-3-flash",
name: "Gemini 3 Flash (OmniRoute)",
envKey: "GEMINI_API_KEY",
baseUrl: normalizedBaseUrl,
};
if (!existingConfig.modelProviders.gemini) existingConfig.modelProviders.gemini = [];
const geminiProviders = existingConfig.modelProviders.gemini;
const geminiIdx = geminiProviders.findIndex((p: any) => p && p.baseUrl === normalizedBaseUrl);
if (geminiIdx >= 0) {
geminiProviders[geminiIdx] = geminiEntry;
} else {
geminiProviders.push(geminiEntry);
}
await fs.writeFile(settingsPath, JSON.stringify(existingConfig, null, 2), "utf-8");
// Persist last-configured timestamp
try {
saveCliToolLastConfigured("qwen");
} catch {
/* non-critical */
} catch (error) {
logger.warn({ err: error }, "Failed to save Qwen Code configuration timestamp");
}
return NextResponse.json({
success: true,
message: "Qwen Code config saved successfully!",
settingsPath,
envPath,
message: "Qwen Code now routes through OmniRoute",
settingsPath: configPaths.settings,
envPath: configPaths.env,
});
} catch (error) {
console.log("Error updating qwen settings:", error);
return NextResponse.json({ error: "Failed to update qwen settings" }, { status: 500 });
logger.error({ err: error }, "Failed to update Qwen Code settings");
return NextResponse.json(
{ error: sanitizeErrorMessage(error instanceof Error ? error.message : String(error)) },
{ status: 500 }
);
}
}
// DELETE - Remove OmniRoute config from settings.json and .env
export async function DELETE(request: Request) {
export async function DELETE(request: Request): Promise<Response> {
const authError = await requireCliToolsAuth(request);
if (authError) return authError;
const writeGuard = ensureCliConfigWriteAllowed();
if (writeGuard) return NextResponse.json({ error: writeGuard }, { status: 403 });
try {
const writeGuard = ensureCliConfigWriteAllowed();
if (writeGuard) {
return NextResponse.json({ error: writeGuard }, { status: 403 });
}
const configPaths = getPaths();
const [settingsExists, envExists, existingSettings, existingEnv] = await Promise.all([
fileExists(configPaths.settings),
fileExists(configPaths.env),
readSettings(configPaths.settings),
readTextIfPresent(configPaths.env),
]);
const nextSettings = removeQwenCodeSettings(existingSettings);
const nextEnv = removeQwenCodeEnv(existingEnv);
const settingsPath = getQwenSettingsPath();
const envPath = getQwenEnvPath();
// Backup current settings before resetting
await createBackup("qwen", settingsPath);
// --- Clean settings.json ---
let existingConfig: Record<string, any> = {};
try {
const raw = await fs.readFile(settingsPath, "utf-8");
existingConfig = JSON.parse(raw);
} catch (error: any) {
if (error.code === "ENOENT") {
return NextResponse.json({
success: true,
message: "No settings file to reset",
});
if (settingsExists || envExists) {
await createMultiBackup("qwen", [configPaths.settings, configPaths.env]);
if (settingsExists) {
await writeAtomic(configPaths.settings, `${JSON.stringify(nextSettings, null, 2)}\n`);
}
throw error;
if (envExists) await writeAtomic(configPaths.env, nextEnv, 0o600);
}
// Remove OmniRoute entries from each provider type
const providerTypes = ["openai", "anthropic", "gemini"];
for (const type of providerTypes) {
if (Array.isArray(existingConfig.modelProviders?.[type])) {
existingConfig.modelProviders[type] = existingConfig.modelProviders[type].filter(
(p: any) => !p.name?.includes("OmniRoute") && p.id !== "omniroute"
);
// Remove empty provider arrays
if (existingConfig.modelProviders[type].length === 0) {
delete existingConfig.modelProviders[type];
}
}
}
// Clean up empty modelProviders
if (existingConfig.modelProviders && Object.keys(existingConfig.modelProviders).length === 0) {
delete existingConfig.modelProviders;
}
await fs.writeFile(settingsPath, JSON.stringify(existingConfig, null, 2), "utf-8");
// --- Clean .env ---
const RESET_ENV_KEYS = ["OPENAI_API_KEY", "ANTHROPIC_API_KEY", "GEMINI_API_KEY"];
try {
let envContent = await fs.readFile(envPath, "utf-8");
const envLines = envContent
.split("\n")
.filter((line) => !RESET_ENV_KEYS.some((key) => line.startsWith(`${key}=`)));
await fs.writeFile(envPath, envLines.join("\n").trim() + "\n", "utf-8");
} catch {
// .env doesn't exist — nothing to clean
}
// Clear last-configured timestamp
try {
deleteCliToolLastConfigured("qwen");
} catch {
/* non-critical */
} catch (error) {
logger.warn({ err: error }, "Failed to clear Qwen Code configuration timestamp");
}
return NextResponse.json({
@@ -357,7 +213,10 @@ export async function DELETE(request: Request) {
message: "OmniRoute settings removed from Qwen Code",
});
} catch (error) {
console.log("Error resetting qwen settings:", error);
return NextResponse.json({ error: "Failed to reset qwen settings" }, { status: 500 });
logger.error({ err: error }, "Failed to reset Qwen Code settings");
return NextResponse.json(
{ error: sanitizeErrorMessage(error instanceof Error ? error.message : String(error)) },
{ status: 500 }
);
}
}

View File

@@ -60,8 +60,8 @@ export async function GET(request: Request) {
"openclaw",
"cline",
"kilo",
"qwen",
"hermes",
"qwen",
];
await Promise.all(

View File

@@ -139,16 +139,8 @@ export const PROVIDER_MODELS_CONFIG: Record<string, ProviderModelsConfigEntry> =
authPrefix: "Bearer ",
parseResponse: (data) => normalizeOpenAiLikeModelsResponse(data, "huggingface"),
},
qwen: {
url: "https://dashscope-intl.aliyuncs.com/compatible-mode/v1/models",
method: "GET",
headers: { "Content-Type": "application/json" },
authHeader: "Authorization",
authPrefix: "Bearer ",
parseResponse: (data) => data.data || [],
},
// #3931: qwen-web (cookie provider) was missing here, so its discovery page
// showed nothing (the OAuth fallback above only fires for provider==="qwen").
// showed nothing.
// `chat.qwen.ai/api/v2/models/` is public (no auth header configured/sent);
// shape `{ data: { data: [{ id, name, owned_by }] } }`, flatter `{ data: [] }` fallback.
"qwen-web": {
@@ -226,8 +218,7 @@ export const PROVIDER_MODELS_CONFIG: Record<string, ProviderModelsConfigEntry> =
if (token) out["x-api-key"] = token;
return out;
},
parseResponse: (data: any) =>
Array.isArray(data) ? data : (data?.data || data?.models || []),
parseResponse: (data: any) => (Array.isArray(data) ? data : data?.data || data?.models || []),
},
openai: {
url: "https://api.openai.com/v1/models",

View File

@@ -1870,25 +1870,6 @@ export async function GET(
provider in PROVIDER_MODELS_CONFIG
? PROVIDER_MODELS_CONFIG[provider as keyof typeof PROVIDER_MODELS_CONFIG]
: deriveConfigFromRegistryModelsUrl(provider);
// Static model providers (no remote /models API)
// Qwen OAuth Fallback: The Dashscope /models API rejects OAuth tokens with 401
if (provider === "qwen" && connection.authType === "oauth") {
const qwenModels = getModelsByProviderId("qwen");
return buildResponse({
provider,
connectionId,
models: qwenModels.map((m: any) => ({
id: m.id,
name: m.name || m.id,
owned_by: "qwen",
})),
source: "local_catalog",
// #5460/#5465 — Qwen OAuth has no OAuth-compatible remote /models list;
// the static catalog is intentional, so model-sync should import it.
intentional: true,
});
}
if (provider === "codex") {
// Auto-merge live/GitHub/local (future-proof discovery), then apply explicit
// denylist filters (e.g. drop GPT-5.4 family). Do not gate remote-only IDs.

Some files were not shown because too many files have changed in this diff Show More