Fix Kimi usage credential lookup
This commit is contained in:
@@ -67,6 +67,8 @@ To add plan usage for a provider, add `packages/server/src/services/quota-fetche
|
|||||||
|
|
||||||
Keep the protocol shape provider-agnostic. Do not add provider-specific renderers for new limit windows; labels and generic bars should carry the UI. API responses should be parsed and normalized with Zod inside the fetcher, while the protocol boundary stays strict so old/new client compatibility is explicit.
|
Keep the protocol shape provider-agnostic. Do not add provider-specific renderers for new limit windows; labels and generic bars should carry the UI. API responses should be parsed and normalized with Zod inside the fetcher, while the protocol boundary stays strict so old/new client compatibility is explicit.
|
||||||
|
|
||||||
|
Kimi Code usage follows the CLI-managed credential file at `KIMI_CODE_HOME` or `~/.kimi-code/credentials/kimi-code.json`; do not probe the legacy `~/.kimi` path as the primary source for current Kimi Code installs.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## ACP Provider Checklist
|
## ACP Provider Checklist
|
||||||
|
|||||||
@@ -88,13 +88,24 @@ export class KimiQuotaProvider implements ProviderUsageFetcher {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async readKimiToken(): Promise<string | null> {
|
private async readKimiToken(): Promise<string | null> {
|
||||||
const path = join(homedir(), ".kimi", "credentials", "kimi-code.json");
|
const paths = [
|
||||||
if (!existsSync(path)) return null;
|
join(
|
||||||
try {
|
process.env["KIMI_CODE_HOME"] || join(homedir(), ".kimi-code"),
|
||||||
const credentials = KimiAuthSchema.parse(JSON.parse(await fs.readFile(path, "utf8")));
|
"credentials",
|
||||||
return credentials.access_token ?? null;
|
"kimi-code.json",
|
||||||
} catch {
|
),
|
||||||
return null;
|
join(homedir(), ".kimi", "credentials", "kimi-code.json"),
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const path of paths) {
|
||||||
|
if (!existsSync(path)) continue;
|
||||||
|
try {
|
||||||
|
const credentials = KimiAuthSchema.parse(JSON.parse(await fs.readFile(path, "utf8")));
|
||||||
|
if (credentials.access_token) return credentials.access_token;
|
||||||
|
} catch {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { readFileSync, writeFileSync } from "node:fs";
|
import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
||||||
import { mkdtempSync, rmSync } from "node:fs";
|
import { mkdtempSync, rmSync } from "node:fs";
|
||||||
import { tmpdir } from "node:os";
|
import { tmpdir } from "node:os";
|
||||||
import { join } from "node:path";
|
import { join } from "node:path";
|
||||||
@@ -36,6 +36,20 @@ function writeCodexAuth(dir: string, accessToken: string, refreshToken = "rt_cod
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function writeKimiCredentials(dir: string, accessToken: string): void {
|
||||||
|
mkdirSync(join(dir, "credentials"), { recursive: true });
|
||||||
|
writeFileSync(
|
||||||
|
join(dir, "credentials", "kimi-code.json"),
|
||||||
|
JSON.stringify({
|
||||||
|
access_token: accessToken,
|
||||||
|
refresh_token: "rt_kimi",
|
||||||
|
expires_at: 1_798_812_800,
|
||||||
|
scope: "kimi-code",
|
||||||
|
token_type: "Bearer",
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function makeClaudeResponse(
|
function makeClaudeResponse(
|
||||||
overrides: Partial<{
|
overrides: Partial<{
|
||||||
five_hour: { utilization: number | string; resets_at: string };
|
five_hour: { utilization: number | string; resets_at: string };
|
||||||
@@ -299,6 +313,7 @@ describe("real provider usage fetchers", () => {
|
|||||||
"GROK_TOKEN",
|
"GROK_TOKEN",
|
||||||
"KIMI_TOKEN",
|
"KIMI_TOKEN",
|
||||||
"KIMI_API_KEY",
|
"KIMI_API_KEY",
|
||||||
|
"KIMI_CODE_HOME",
|
||||||
"CODEX_HOME",
|
"CODEX_HOME",
|
||||||
]) {
|
]) {
|
||||||
delete process.env[key];
|
delete process.env[key];
|
||||||
@@ -694,4 +709,35 @@ describe("real provider usage fetchers", () => {
|
|||||||
],
|
],
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("fetches Kimi usage from the CLI credential home", async () => {
|
||||||
|
writeKimiCredentials(join(homeDir, ".kimi-code"), "kimi_cli_token");
|
||||||
|
fetchApi = vi.fn(async (url: RequestInfo | URL, init?: RequestInit) => {
|
||||||
|
expect(url.toString()).toBe("https://api.kimi.com/coding/v1/usages");
|
||||||
|
expect((init?.headers as Record<string, string> | undefined)?.Authorization).toBe(
|
||||||
|
"Bearer kimi_cli_token",
|
||||||
|
);
|
||||||
|
return jsonResponse({
|
||||||
|
usage: {
|
||||||
|
limit: "200",
|
||||||
|
remaining: "150",
|
||||||
|
resetTime: "2026-06-23T05:12:17Z",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}) as unknown as typeof fetch;
|
||||||
|
|
||||||
|
const kimi = findProvider(await service().listUsage(), "kimi");
|
||||||
|
|
||||||
|
expect(kimi).toMatchObject({
|
||||||
|
status: "available",
|
||||||
|
windows: [
|
||||||
|
expect.objectContaining({
|
||||||
|
id: "coding_usage",
|
||||||
|
usedPct: 25,
|
||||||
|
remainingPct: 75,
|
||||||
|
resetsAt: "2026-06-23T05:12:17Z",
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user