fix(usage): narrow credential discovery repair to the verified GLM path

Keep the independently reproduced GLM discovery fix and isolate its file test from inherited XDG_CONFIG_HOME. Leave MiniMax quota routing for separate verification before changing existing API-key requests.

Co-authored-by: peanut_hzm <peanut_hzm@163.com>
This commit is contained in:
paseo-bot[bot]
2026-10-31 11:56:21 +00:00
co-authored by peanut_hzm
parent 32a420db1b
commit 0e60432de9
4 changed files with 11 additions and 289 deletions
@@ -23,60 +23,6 @@ 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();
@@ -185,7 +131,7 @@ describe("minimax usage source", () => {
const miniMax = findProvider(await service().listUsage(), "minimax");
expect(requestedUrl).toBe("https://api.minimax.io/v1/api/openplatform/coding_plan/remains");
expect(requestedUrl).toBe("https://api.minimax.io/v1/token_plan/remains");
expect(authorization).toBe("Bearer minimax_test_token");
expect(miniMax).toMatchObject({
status: "available",
@@ -212,88 +158,6 @@ 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,
@@ -309,9 +173,7 @@ describe("minimax usage source", () => {
await service().listUsage();
expect(requestedUrl).toBe(
"https://account.example.com/v1/api/openplatform/coding_plan/remains",
);
expect(requestedUrl).toBe("https://account.example.com/v1/token_plan/remains");
});
it("falls back to MiniMax api_key in the CLI config file", async () => {
@@ -327,7 +189,7 @@ describe("minimax usage source", () => {
const miniMax = findProvider(await service().listUsage(), "minimax");
expect(requestedUrl).toBe("https://api.minimaxi.com/v1/api/openplatform/coding_plan/remains");
expect(requestedUrl).toBe("https://api.minimaxi.com/v1/token_plan/remains");
expect(miniMax.status).toBe("unavailable");
});
@@ -336,7 +198,7 @@ describe("minimax usage source", () => {
fetchApi = mockFetch(
new Map([
[
"https://api.minimaxi.com/v1/api/openplatform/coding_plan/remains",
"https://api.minimaxi.com/v1/token_plan/remains",
() =>
jsonResponse({
model_remains: null,
@@ -360,7 +222,7 @@ describe("minimax usage source", () => {
fetchApi = mockFetch(
new Map([
[
"https://api.minimax.io/v1/api/openplatform/coding_plan/remains",
"https://api.minimax.io/v1/token_plan/remains",
() => jsonResponse({ model_remains: null }),
],
]),
@@ -380,7 +242,7 @@ describe("minimax usage source", () => {
fetchApi = mockFetch(
new Map([
[
"https://api.minimax.io/v1/api/openplatform/coding_plan/remains",
"https://api.minimax.io/v1/token_plan/remains",
() =>
jsonResponse({
base_resp: { status_code: 0, status_msg: "success" },
@@ -413,7 +275,7 @@ describe("minimax usage source", () => {
fetchApi = mockFetch(
new Map([
[
"https://api.minimax.io/v1/api/openplatform/coding_plan/remains",
"https://api.minimax.io/v1/token_plan/remains",
() =>
jsonResponse({
model_remains: [
+2 -138
View File
@@ -1,7 +1,7 @@
import type { UsageInput } from "../shared/input.js";
import { promises as fs } from "node:fs";
import { homedir } from "node:os";
import { join, dirname } from "node:path";
import { join } from "node:path";
import { z } from "zod";
import {
unavailable,
@@ -20,10 +20,6 @@ const ApiOptionalStringSchema = z.preprocess(
const MINIMAX_GLOBAL_BASE_URL = "https://api.minimax.io";
const MINIMAX_CN_BASE_URL = "https://api.minimaxi.com";
// The coding-plan quota endpoint the CLI itself calls. The older
// `/v1/token_plan/remains` path is not the one the agent backend serves.
const MINIMAX_CODING_PLAN_PATH = "/v1/api/openplatform/coding_plan/remains";
const MiniMaxModelRemainSchema = z.object({
model_name: ApiOptionalStringSchema,
start_time: ApiNumberSchema.optional(),
@@ -72,31 +68,6 @@ const MiniMaxConfigSchema = z.object({
oauth: MiniMaxCredentialsSchema.optional(),
});
/**
* The `mcode` CLI stores its OAuth login under `~/.minimax/auth/<env>/<region>/<clientId>/`.
* Each client directory holds an `auth.json` with the token and an
* `auth-state.json` with the region and expiry.
*/
const MiniMaxOAuthAuthSchema = z.object({
schemaVersion: z.number().optional(),
records: z.record(
z.string(),
z.object({
accessToken: z.string().optional(),
refreshToken: z.string().optional(),
expiresAtMs: z.number().optional(),
}),
),
});
const MiniMaxOAuthStateSchema = z.object({
status: z.string().optional(),
clientId: z.string().optional(),
buildEnv: z.string().optional(),
region: z.string().optional(),
expiresAtMs: z.number().optional(),
});
type MiniMaxModelRemain = z.infer<typeof MiniMaxModelRemainSchema>;
interface MiniMaxResolvedAuth {
@@ -183,7 +154,7 @@ export async function fetchUsage(
if (auth.expiresAt && Date.parse(auth.expiresAt) <= Date.now())
return unavailable({ kind: "expired", expiresAt: new Date(auth.expiresAt).toISOString() });
const res = await fetchApi(`${auth.baseUrl}${MINIMAX_CODING_PLAN_PATH}`, {
const res = await fetchApi(`${auth.baseUrl}/v1/token_plan/remains`, {
signal: AbortSignal.timeout(15_000),
headers: {
Authorization: `Bearer ${auth.token}`,
@@ -236,9 +207,6 @@ async function readAuth(
? { token, baseUrl: resolveBaseUrl({ baseUrl: process.env.MINIMAX_BASE_URL }) }
: undefined;
}
if (input.store === "oauth") {
return readOAuthAuth(input);
}
try {
const raw: unknown = JSON.parse(await fs.readFile(input.locator, "utf8"));
if (input.store === "credentials") {
@@ -271,101 +239,6 @@ async function readAuth(
return undefined;
}
}
/**
* Reads the OAuth login the `mcode` CLI writes to
* `~/.minimax/auth/<buildEnv>/<region>/<clientId>/auth.json`. The region
* decides which host serves the account, and the token lives inside a
* `records` map keyed by the OAuth grant.
*/
async function readOAuthAuth(
input: UsageInput,
): Promise<(MiniMaxResolvedAuth & { expiresAt?: string }) | undefined> {
try {
const raw: unknown = JSON.parse(await fs.readFile(input.locator, "utf8"));
const auth = MiniMaxOAuthAuthSchema.parse(raw);
const record = Object.values(auth.records).find((entry) => entry.accessToken);
if (!record?.accessToken) return undefined;
const state = await readOAuthState(input.locator);
const expiresAtMs = record.expiresAtMs ?? state?.expiresAtMs;
return {
token: record.accessToken,
baseUrl: resolveBaseUrl({
baseUrl: input.baseUrl,
region: state?.region,
}),
expiresAt: typeof expiresAtMs === "number" ? new Date(expiresAtMs).toISOString() : undefined,
};
} catch {
return undefined;
}
}
async function readOAuthState(
authPath: string,
): Promise<z.infer<typeof MiniMaxOAuthStateSchema> | undefined> {
try {
const statePath = join(dirname(authPath), "auth-state.json");
const raw: unknown = JSON.parse(await fs.readFile(statePath, "utf8"));
return MiniMaxOAuthStateSchema.parse(raw);
} catch {
return undefined;
}
}
/**
* Locates the `mcode` CLI's OAuth login. The layout is
* `~/.minimax/auth/<buildEnv>/<region>/<clientId>/auth.json`, so the client
* directory is found by scanning the region directories rather than by
* assuming a fixed client id.
*/
async function discoverOAuthLogins(): Promise<UsageInput[]> {
const authRoot = join(homedir(), ".minimax", "auth");
const inputs: UsageInput[] = [];
let buildEnvDirs: string[];
try {
buildEnvDirs = (await fs.readdir(authRoot, { withFileTypes: true }))
.filter((entry) => entry.isDirectory())
.map((entry) => entry.name);
} catch {
return inputs;
}
for (const buildEnv of buildEnvDirs) {
const envRoot = join(authRoot, buildEnv);
let regionDirs: string[];
try {
regionDirs = (await fs.readdir(envRoot, { withFileTypes: true }))
.filter((entry) => entry.isDirectory())
.map((entry) => entry.name);
} catch {
continue;
}
for (const region of regionDirs) {
const regionRoot = join(envRoot, region);
let clientDirs: string[];
try {
clientDirs = (await fs.readdir(regionRoot, { withFileTypes: true }))
.filter((entry) => entry.isDirectory())
.map((entry) => entry.name);
} catch {
continue;
}
for (const clientId of clientDirs) {
const authPath = join(regionRoot, clientId, "auth.json");
const auth = await readOAuthAuth({ store: "oauth", locator: authPath });
if (!auth) continue;
inputs.push({
store: "oauth",
locator: authPath,
baseUrl: auth.baseUrl,
});
}
}
}
return inputs;
}
export async function discover(): Promise<UsageAccount[]> {
const candidates: UsageInput[] = [
{ store: "env", locator: "MINIMAX_API_KEY" },
@@ -375,14 +248,5 @@ export async function discover(): Promise<UsageAccount[]> {
const accounts: UsageAccount[] = [];
for (const input of candidates)
if (await readAuth(input)) accounts.push({ key: "default", input });
if (accounts.length > 0) return accounts;
const oauthInputs = await discoverOAuthLogins();
for (const input of oauthInputs) {
if (await readAuth(input)) {
accounts.push({ key: "default", input });
break;
}
}
return accounts;
}
+1 -6
View File
@@ -1,13 +1,8 @@
import { z } from "zod";
export const inputSchema = z
.object({
store: z.enum(["env", "credentials", "config", "oauth"]),
store: z.enum(["env", "credentials", "config"]),
locator: z.string().min(1),
/**
* Base URL for the coding-plan quota endpoint. The CLI's OAuth login is
* region-scoped (cn vs en), and each region serves its own host.
*/
baseUrl: z.string().optional(),
})
.strict();
export type UsageInput = z.infer<typeof inputSchema>;
@@ -32,6 +32,7 @@ describe("zai usage source", () => {
process.env["USERPROFILE"] = homeDir;
for (const key of [
"APPDATA",
"XDG_CONFIG_HOME",
"COPILOT_TOKEN",
"GITHUB_TOKEN",
"GITHUB_PAT",