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

@@ -77,6 +77,8 @@ export async function getUsageForProvider(connection, proxyOptions = null) {
return await getCodexUsage(accessToken, proxyOptions);
case "kiro":
return await getKiroUsage(accessToken, providerSpecificData, proxyOptions);
case "qoder":
return await getQoderUsage(accessToken, proxyOptions);
case "qwen":
return await getQwenUsage(accessToken, providerSpecificData);
case "iflow":
@@ -1149,3 +1151,51 @@ async function getMiniMaxUsage(apiKey, provider, proxyOptions = null) {
return { message: lastErrorMessage ? `MiniMax connected. Unable to fetch usage: ${lastErrorMessage}` : "MiniMax connected. Unable to fetch usage." };
}
async function getQoderUsage(accessToken, proxyOptions = null) {
if (!accessToken) {
return { message: "Qoder usage unavailable: no access token" };
}
try {
const response = await proxyAwareFetch(
"https://openapi.qoder.sh/api/v2/quota/usage",
{
method: "GET",
headers: {
Authorization: `Bearer ${accessToken}`,
Accept: "application/json",
},
},
proxyOptions,
);
if (!response.ok) {
return { message: `Qoder connected. Usage fetch returned ${response.status}.` };
}
const body = await response.json().catch(() => null);
if (!body) {
return { message: "Qoder connected. Usage response was not JSON." };
}
const userQuota = body.userQuota || {};
const orgQuota = body.orgResourcePackage || {};
const quotas = {
user: {
total: Number(userQuota.total) || 0,
used: Number(userQuota.used) || 0,
remaining: Number(userQuota.remaining) || 0,
unit: userQuota.unit || "credits",
},
organization: {
total: Number(orgQuota.total) || 0,
used: Number(orgQuota.used) || 0,
remaining: Number(orgQuota.remaining) || 0,
unit: orgQuota.unit || "credits",
},
totalUsagePercentage: Number(body.totalUsagePercentage) || 0,
isQuotaExceeded: !!body.isQuotaExceeded,
expiresAt: Number(body.expiresAt) || null,
};
return { quotas };
} catch (error) {
return { message: `Qoder connected. Unable to fetch usage: ${error.message}` };
}
}