mirror of
https://github.com/getpaseo/paseo.git
synced 2026-11-11 10:57:07 +00:00
fix(usage): follow the CLIs' current auth locations for MiniMax and Z.ai
The usage sources look for credentials where the CLIs used to keep them, so
their plan quota never appears in Settings → Usage.
MiniMax Code (`mcode`) moved its OAuth login to
`~/.minimax/auth/<buildEnv>/<region>/<clientId>/auth.json`, with the token
inside a `records` map and the region in a sibling `auth-state.json`. The
source still looked in `~/.mmx/` for the older `access_token` shape. It also
called `/v1/token_plan/remains`, which the agent backend answers with
`status_code 1004` ("Please carry the API secret key in the 'Authorization'
field"); the coding-plan endpoint the CLI itself uses is
`/v1/api/openplatform/coding_plan/remains`.
Z.ai's `glm-acp-agent` reads `Z_AI_API_KEY` and persists it to
`~/.config/glm-acp-agent/credentials.json` as `z_ai_api_key`. The source
looked for `ZAI_API_KEY` and `GLM_API_KEY` only, and never read the
credentials file.
Both keep their existing lookups working, so a configured environment
variable or the older `~/.mmx/` layout still resolves.
This commit is contained in:
@@ -23,6 +23,60 @@ function writeMiniMaxCredentials(
|
||||
writeFileSync(join(dir, ".mmx", "credentials.json"), JSON.stringify(payload));
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the OAuth login the `mcode` CLI keeps under
|
||||
* `~/.minimax/auth/<buildEnv>/<region>/<clientId>/`.
|
||||
*/
|
||||
function writeMiniMaxOAuthLogin(
|
||||
dir: string,
|
||||
options: {
|
||||
buildEnv?: string;
|
||||
region?: string;
|
||||
clientId?: string;
|
||||
accessToken?: string;
|
||||
expiresAtMs?: number;
|
||||
} = {},
|
||||
): string {
|
||||
const buildEnv = options.buildEnv ?? "prod";
|
||||
const region = options.region ?? "cn";
|
||||
const clientId = options.clientId ?? "mcode-public";
|
||||
const clientDir = join(dir, ".minimax", "auth", buildEnv, region, clientId);
|
||||
mkdirSync(clientDir, { recursive: true });
|
||||
writeFileSync(
|
||||
join(clientDir, "auth.json"),
|
||||
JSON.stringify({
|
||||
schemaVersion: 1,
|
||||
records: {
|
||||
"com.minimax.mcode.oauth.prod.cn": {
|
||||
schemaVersion: 1,
|
||||
accessToken: options.accessToken ?? "mmoat_fixture_token",
|
||||
refreshToken: "mmort_fixture_token",
|
||||
tokenType: "Bearer",
|
||||
clientId,
|
||||
scopes: ["agent.default"],
|
||||
audience: "agent-backend",
|
||||
...(options.expiresAtMs === undefined ? {} : { expiresAtMs: options.expiresAtMs }),
|
||||
},
|
||||
},
|
||||
}),
|
||||
);
|
||||
writeFileSync(
|
||||
join(clientDir, "auth-state.json"),
|
||||
JSON.stringify({
|
||||
schemaVersion: 2,
|
||||
status: "authenticated",
|
||||
storeKind: "file",
|
||||
clientId,
|
||||
scopes: ["agent.default"],
|
||||
audience: "agent-backend",
|
||||
buildEnv,
|
||||
region,
|
||||
...(options.expiresAtMs === undefined ? {} : { expiresAtMs: options.expiresAtMs }),
|
||||
}),
|
||||
);
|
||||
return join(clientDir, "auth.json");
|
||||
}
|
||||
|
||||
function mockFetch(handlers: Map<string, () => Response>): typeof fetch {
|
||||
return vi.fn(async (url: RequestInfo | URL) => {
|
||||
const key = url.toString();
|
||||
@@ -131,7 +185,7 @@ describe("minimax usage source", () => {
|
||||
|
||||
const miniMax = findProvider(await service().listUsage(), "minimax");
|
||||
|
||||
expect(requestedUrl).toBe("https://api.minimax.io/v1/token_plan/remains");
|
||||
expect(requestedUrl).toBe("https://api.minimax.io/v1/api/openplatform/coding_plan/remains");
|
||||
expect(authorization).toBe("Bearer minimax_test_token");
|
||||
expect(miniMax).toMatchObject({
|
||||
status: "available",
|
||||
@@ -158,6 +212,88 @@ describe("minimax usage source", () => {
|
||||
expect(await discover()).toEqual([]);
|
||||
});
|
||||
|
||||
it("discovers the OAuth login the mcode CLI stores under ~/.minimax/auth", async () => {
|
||||
const authPath = writeMiniMaxOAuthLogin(homeDir, {
|
||||
accessToken: "mmoat_fixture_token",
|
||||
});
|
||||
|
||||
const accounts = await discover();
|
||||
|
||||
expect(accounts).toHaveLength(1);
|
||||
expect(accounts[0]?.input).toEqual({
|
||||
store: "oauth",
|
||||
locator: authPath,
|
||||
baseUrl: "https://api.minimaxi.com",
|
||||
});
|
||||
});
|
||||
|
||||
it("uses the region host the CLI logged in against", async () => {
|
||||
writeMiniMaxOAuthLogin(homeDir, { region: "en" });
|
||||
|
||||
const accounts = await discover();
|
||||
|
||||
expect(accounts[0]?.input).toMatchObject({ baseUrl: "https://api.minimax.io" });
|
||||
});
|
||||
|
||||
it("fetches MiniMax usage from the CLI OAuth login", async () => {
|
||||
writeMiniMaxOAuthLogin(homeDir, { accessToken: "mmoat_fixture_token" });
|
||||
let requestedUrl: string | null = null;
|
||||
let authorization: string | null = null;
|
||||
fetchApi = (async (url: RequestInfo | URL, init?: RequestInit) => {
|
||||
requestedUrl = url.toString();
|
||||
authorization = (init?.headers as Record<string, string> | undefined)?.Authorization ?? null;
|
||||
return jsonResponse({
|
||||
model_remains: [
|
||||
{
|
||||
model_name: "general",
|
||||
end_time: Date.parse("2026-06-19T05:00:00.000Z"),
|
||||
weekly_end_time: Date.parse("2026-06-26T00:00:00.000Z"),
|
||||
current_interval_remaining_percent: 87,
|
||||
current_weekly_remaining_percent: 78,
|
||||
},
|
||||
],
|
||||
});
|
||||
}) as unknown as typeof fetch;
|
||||
|
||||
const miniMax = findProvider(await service().listUsage(), "minimax");
|
||||
|
||||
expect(requestedUrl).toBe("https://api.minimaxi.com/v1/api/openplatform/coding_plan/remains");
|
||||
expect(authorization).toBe("Bearer mmoat_fixture_token");
|
||||
expect(miniMax).toMatchObject({
|
||||
status: "available",
|
||||
windows: expect.arrayContaining([
|
||||
expect.objectContaining({ id: "interval_general", usedPct: 13, remainingPct: 87 }),
|
||||
expect.objectContaining({ id: "weekly_general", usedPct: 22, remainingPct: 78 }),
|
||||
]),
|
||||
});
|
||||
});
|
||||
|
||||
it("reports an expired CLI OAuth login as expired", async () => {
|
||||
writeMiniMaxOAuthLogin(homeDir, {
|
||||
accessToken: "mmoat_fixture_token",
|
||||
expiresAtMs: Date.now() - 60_000,
|
||||
});
|
||||
let requested = false;
|
||||
fetchApi = (async () => {
|
||||
requested = true;
|
||||
return jsonResponse({ model_remains: [] });
|
||||
}) as unknown as typeof fetch;
|
||||
|
||||
const miniMax = findProvider(await service().listUsage(), "minimax");
|
||||
|
||||
expect(requested).toBe(false);
|
||||
expect(miniMax).toMatchObject({
|
||||
status: "unavailable",
|
||||
problem: { kind: "expired" },
|
||||
});
|
||||
});
|
||||
|
||||
it("ignores a CLI auth directory that has no access token", async () => {
|
||||
writeMiniMaxOAuthLogin(homeDir, { accessToken: "" });
|
||||
|
||||
expect(await discover()).toEqual([]);
|
||||
});
|
||||
|
||||
it("reads MiniMax OAuth credentials from the CLI credentials file", async () => {
|
||||
writeMiniMaxCredentials(
|
||||
homeDir,
|
||||
@@ -173,7 +309,9 @@ describe("minimax usage source", () => {
|
||||
|
||||
await service().listUsage();
|
||||
|
||||
expect(requestedUrl).toBe("https://account.example.com/v1/token_plan/remains");
|
||||
expect(requestedUrl).toBe(
|
||||
"https://account.example.com/v1/api/openplatform/coding_plan/remains",
|
||||
);
|
||||
});
|
||||
|
||||
it("falls back to MiniMax api_key in the CLI config file", async () => {
|
||||
@@ -189,7 +327,7 @@ describe("minimax usage source", () => {
|
||||
|
||||
const miniMax = findProvider(await service().listUsage(), "minimax");
|
||||
|
||||
expect(requestedUrl).toBe("https://api.minimaxi.com/v1/token_plan/remains");
|
||||
expect(requestedUrl).toBe("https://api.minimaxi.com/v1/api/openplatform/coding_plan/remains");
|
||||
expect(miniMax.status).toBe("unavailable");
|
||||
});
|
||||
|
||||
@@ -198,7 +336,7 @@ describe("minimax usage source", () => {
|
||||
fetchApi = mockFetch(
|
||||
new Map([
|
||||
[
|
||||
"https://api.minimaxi.com/v1/token_plan/remains",
|
||||
"https://api.minimaxi.com/v1/api/openplatform/coding_plan/remains",
|
||||
() =>
|
||||
jsonResponse({
|
||||
model_remains: null,
|
||||
@@ -222,7 +360,7 @@ describe("minimax usage source", () => {
|
||||
fetchApi = mockFetch(
|
||||
new Map([
|
||||
[
|
||||
"https://api.minimax.io/v1/token_plan/remains",
|
||||
"https://api.minimax.io/v1/api/openplatform/coding_plan/remains",
|
||||
() => jsonResponse({ model_remains: null }),
|
||||
],
|
||||
]),
|
||||
@@ -242,7 +380,7 @@ describe("minimax usage source", () => {
|
||||
fetchApi = mockFetch(
|
||||
new Map([
|
||||
[
|
||||
"https://api.minimax.io/v1/token_plan/remains",
|
||||
"https://api.minimax.io/v1/api/openplatform/coding_plan/remains",
|
||||
() =>
|
||||
jsonResponse({
|
||||
base_resp: { status_code: 0, status_msg: "success" },
|
||||
@@ -275,7 +413,7 @@ describe("minimax usage source", () => {
|
||||
fetchApi = mockFetch(
|
||||
new Map([
|
||||
[
|
||||
"https://api.minimax.io/v1/token_plan/remains",
|
||||
"https://api.minimax.io/v1/api/openplatform/coding_plan/remains",
|
||||
() =>
|
||||
jsonResponse({
|
||||
model_remains: [
|
||||
|
||||
Reference in New Issue
Block a user