feat(qoder): port Kiro-style provider integration with COSY signing

Replaces the Qoder placeholder with a real free-tier provider:

- Device-flow OAuth: PKCE + nonce generated locally, user authorizes at
  qoder.com/device/selectAccounts, poll openapi.qoder.sh until token
- COSY signing (RSA-1024 + AES-128-CBC + MD5) for chat / model-list
- WAF-bypass body encoding (custom-alphabet base64 + thirds rearrange)
- Live model_config catalog from /algo/api/v2/model/list, cached 1h
- 11 models registered (auto/ultimate/performance/efficient/lite +
  6 frontier *model ids)
- Usage fetcher for openapi.qoder.sh/api/v2/quota/usage
- Dashboard live-models resolver, provider test, OAuth modal hookup
- 24 unit tests covering encoder, PKCE, COSY headers, sigPath stripping
This commit is contained in:
Simon Shi
2026-05-23 16:31:50 +09:00
committed by decolua
parent 468c61b2ac
commit a6fd84691b
20 changed files with 1506 additions and 132 deletions

View File

@@ -144,6 +144,21 @@ export const PROVIDER_MODELS = {
{ id: "claude-sonnet-4.5-thinking-agentic", name: "Claude Sonnet 4.5 (Thinking + Agentic)" },
{ id: "claude-haiku-4.5-thinking-agentic", name: "Claude Haiku 4.5 (Thinking + Agentic)" },
],
qd: [ // Qoder AI - tier + frontier models (server-published catalog)
// Tier models — pick a quality/cost tradeoff
{ id: "auto", name: "Qoder Auto" },
{ id: "ultimate", name: "Qoder Ultimate" },
{ id: "performance", name: "Qoder Performance" },
{ id: "efficient", name: "Qoder Efficient" },
{ id: "lite", name: "Qoder Lite" },
// Frontier models — pin a specific backing model
{ id: "qmodel", name: "Qwen 3.6 Plus (Qoder)" },
{ id: "dmodel", name: "DeepSeek V4 Pro (Qoder)" },
{ id: "dfmodel", name: "DeepSeek V4 Flash (Qoder)" },
{ id: "gm51model", name: "GLM 5.1 (Qoder)" },
{ id: "kmodel", name: "Kimi K2.6 (Qoder)" },
{ id: "mmodel", name: "MiniMax M2.7 (Qoder)" },
],
cu: [ // Cursor IDE
{ id: "default", name: "Auto (Server Picks)" },
{ id: "claude-4.5-opus-high-thinking", name: "Claude 4.5 Opus High Thinking" },
@@ -870,6 +885,7 @@ const OAUTH_ALIASES = {
kilocode: "kc",
cline: "cl",
opencode: "oc",
qoder: "qd",
vertex: "vertex",
"vertex-partner": "vertex-partner",
};

View File

@@ -94,13 +94,13 @@ export const PROVIDERS = {
authUrl: "https://iflow.cn/oauth"
},
qoder: {
baseUrl: "https://api.qoder.com/v1/chat/completions",
// The qoder executor builds the full URL itself (it has to append
// ?Encode=1 + sigPath query params and bypass any provider-level URL
// rewriting). baseUrl is kept for compatibility with introspection
// helpers but the executor ignores it.
baseUrl: "https://api3.qoder.sh/algo/api/v2/service/pro/sse/agent_chat_generation",
format: "openai",
headers: { "User-Agent": "Qoder-Cli" },
clientId: process.env.QODER_OAUTH_CLIENT_ID || "10009311001",
clientSecret: process.env.QODER_OAUTH_CLIENT_SECRET || "4Z3YjXycVsQvyGF1etiNlIBB4RsqSDtW",
tokenUrl: "https://api.qoder.com/oauth/token",
authUrl: "https://qoder.com/oauth/authorize"
headers: {},
},
antigravity: {
baseUrls: [