fix(kimi): harden peer-rotation adoption per review

Address review feedback: keep KimiRefreshAuthError internal to the
module, and require an access token on the adopted credential so a
partially written file cannot mask the real refresh failure.
This commit is contained in:
Flávio Martil 2026-08-05 10:54:53 -03:00
parent 1ea0c960a7
commit c2e478691a
2 changed files with 44 additions and 2 deletions

View file

@ -54,7 +54,7 @@ type KimiConfiguredProvider = {
sourceFile: string;
};
export class KimiRefreshAuthError extends Error {
class KimiRefreshAuthError extends Error {
readonly status: number;
constructor(status: number, message: string) {
@ -224,7 +224,7 @@ function adoptPeerRotatedKimiAuth(auth: KimiTokenSet, error: unknown): KimiToken
// is stale and the server rejects it, but the file on disk already holds
// the newer credential. Adopt it instead of failing the request.
const latest = readKimiAuthFromFile(auth.sourceFile, auth.oauthHost);
if (latest?.refreshToken && latest.refreshToken !== auth.refreshToken) {
if (latest?.refreshToken && latest.accessToken && latest.refreshToken !== auth.refreshToken) {
return latest;
}
throw error;

View file

@ -221,6 +221,48 @@ oauth = { storage = "file", key = "oauth/kimi-code", oauth_host = "http://127.0.
});
});
test("Kimi CLI OAuth does not adopt a rotated credential without an access token", async (t) => {
await withKimiHome(async (kimiHome) => {
writeFileSync(path.join(kimiHome, "config.toml"), `
[providers."managed:kimi-code"]
type = "kimi"
base_url = "https://api.kimi.com/coding/v1"
oauth = { storage = "file", key = "oauth/kimi-code", oauth_host = "http://127.0.0.1" }
`);
const credentialsDir = path.join(kimiHome, "credentials");
mkdirSync(credentialsDir, { recursive: true });
const credentialFile = path.join(credentialsDir, "kimi-code.json");
writeFileSync(credentialFile, JSON.stringify({
access_token: "expired-token",
expires_at: 1,
expires_in: 3600,
refresh_token: "stale-refresh-token"
}));
const previousFetch = globalThis.fetch;
globalThis.fetch = async () => {
// Partial write: a new refresh token landed without an access token.
writeFileSync(credentialFile, JSON.stringify({
expires_at: 1,
expires_in: 7200,
refresh_token: "peer-refresh-token"
}));
return new Response(JSON.stringify({ error: "invalid_grant" }), {
headers: { "content-type": "application/json" },
status: 401
});
};
t.after(() => {
globalThis.fetch = previousFetch;
});
await assert.rejects(
() => resolveKimiAuth({ key: "oauth/kimi-code", oauthHost: "http://127.0.0.1" }),
/HTTP 401/
);
});
});
async function withKimiHome(run) {
const kimiHome = mkdtempSync(path.join(os.tmpdir(), "ccr-kimi-provider-"));
const previous = {