fix(qoder): bifurcate validation by token type — PAT→Cosy, regular API key→dashscope (#3149)

Bifurcate Qoder validation by token type (PAT→Cosy, regular→dashscope) +regression test. Integrated into release/v3.8.10.
This commit is contained in:
Hernan Javier Ardila Sanchez
2026-06-04 19:25:29 +02:00
committed by GitHub
parent d3c753ecbe
commit 28b44b01d4
2 changed files with 77 additions and 2 deletions

View File

@@ -3718,8 +3718,43 @@ export async function validateProviderApiKey({ provider, apiKey, providerSpecifi
// ── Specialty provider validation ──
const SPECIALTY_VALIDATORS = {
jules: validateJulesProvider,
qoder: ({ apiKey, providerSpecificData }: any) =>
validateQoderCliPat({ apiKey, providerSpecificData }),
qoder: async ({ apiKey, providerSpecificData }: any) => {
// Bifurcate validation: PAT tokens use Cosy auth against api1.qoder.sh;
// regular API keys validate against dashscope (OpenAI-compatible endpoint).
const key = (apiKey || "").trim();
if (key.startsWith("pt-")) {
return validateQoderCliPat({ apiKey: key, providerSpecificData });
}
// Non-PAT token → validate against dashscope (Alibaba Cloud).
// The executor routes these tokens to dashscope.aliyuncs.com, so the
// validation must test against dashscope, NOT the Cosy PAT endpoint.
try {
const dashscopeUrl =
"https://dashscope.aliyuncs.com/compatible-mode/v1/models";
const res = await validationRead(
dashscopeUrl,
{
headers: {
Authorization: `Bearer ${key}`,
},
},
false
);
if (res.ok) return { valid: true, error: null };
if (res.status === 401 || res.status === 403) {
return {
valid: false,
error:
"Invalid Qoder API key. Make sure you're using a valid API key from Qoder / Alibaba Cloud Dashscope.",
};
}
// 4xx/5xx other than auth — treat as valid bypass to prevent false
// negatives from transient dashscope issues (consistent with PAT path).
return { valid: true, error: null };
} catch (err: unknown) {
return toValidationErrorResult(err);
}
},
"command-code": validateCommandCodeProvider,
deepgram: validateDeepgramProvider,
assemblyai: validateAssemblyAIProvider,

View File

@@ -188,3 +188,43 @@ test("#2545 gemini validation does not produce /models/models", async () => {
`outbound URL must hit a single /models segment — got ${calls.join(", ")}`
);
});
test("qoder regular API key validates against dashscope, not the Cosy PAT endpoint (#3149)", async () => {
const calls: string[] = [];
globalThis.fetch = async (url: any, init: any) => {
calls.push(String(url));
const auth = new Headers(init?.headers as HeadersInit | undefined).get("authorization");
assert.equal(auth, "Bearer sk-qoder-regular", "dashscope probe must forward the API key");
return new Response(JSON.stringify({ data: [] }), { status: 200 });
};
const result = await validateProviderApiKey({
provider: "qoder",
apiKey: "sk-qoder-regular",
providerSpecificData: {},
});
assert.equal(result.valid, true);
assert.ok(
calls.some((u) => u.includes("dashscope.aliyuncs.com/compatible-mode/v1/models")),
`regular qoder key must validate against dashscope — got ${calls.join(", ")}`
);
assert.ok(
!calls.some((u) => u.includes("api1.qoder.sh")),
"regular (non-PAT) key must not hit the Cosy PAT endpoint"
);
});
test("qoder regular API key surfaces an auth error when dashscope rejects it (#3149)", async () => {
globalThis.fetch = async () =>
new Response(JSON.stringify({ error: { message: "invalid api key" } }), { status: 401 });
const result = await validateProviderApiKey({
provider: "qoder",
apiKey: "sk-qoder-bad",
providerSpecificData: {},
});
assert.equal(result.valid, false);
assert.match(result.error, /Qoder|Dashscope|API key/i);
});