feat: add MiniMax quota fetcher and brand icon (#1662)

* feat(server): add MiniMax quota fetcher

Mirror the live multi-provider quota panel (#1278) for MiniMax. Resolves the
bearer token from $MINIMAX_API_KEY or the MiniMax CLI config at
~/.mmx/config.json (api_key, oauth.access_token) and credentials at
~/.mmx/credentials.json, then queries the /v1/token_plan/remains endpoint on
the configured region (global by default, cn when region='cn'). The response
is normalized into ProviderUsage windows per model_remains entry, surfacing
the interval and weekly limits with their reset times and a danger tone when
the server reports status=2.

* feat(app): add MiniMax brand icon for usage page

The quota panel resolves the icon via resolveProviderIconName, which falls
back to a generic Bot for any provider not in BUILTIN_PROVIDER_ICON_NAMES.
Register 'minimax' as a built-in icon and vendor the official 24×24 brand
mark (single Path, currentColor) as MiniMaxIcon so the usage card stops
showing the generic robot.

* refactor(server): flatten nested ternaries in MiniMax quota fetcher

Greptile review on #1662 called out two nested ternaries in toIntervalWindow
and toWeeklyWindow. Extract a toneForStatus helper that uses an explicit
if/else chain so the same status-to-tone mapping is shared between the
interval and weekly windows, and add the missing trailing newline.

* fix: format
This commit is contained in:
Matteo Pietro Dazzi
2026-06-26 08:14:29 +02:00
committed by GitHub
parent 8c3b709794
commit 862154541a
7 changed files with 467 additions and 0 deletions

View File

@@ -0,0 +1,14 @@
import Svg, { Path } from "react-native-svg";
interface MiniMaxIconProps {
size?: number;
color?: string;
}
export function MiniMaxIcon({ size = 16, color = "currentColor" }: MiniMaxIconProps) {
return (
<Svg width={size} height={size} viewBox="0 0 24 24" fill={color} fillRule="evenodd">
<Path d="M16.278 2c1.156 0 2.093.927 2.093 2.07v12.501a.74.74 0 00.744.709.74.74 0 00.743-.709V9.099a2.06 2.06 0 012.071-2.049A2.06 2.06 0 0124 9.1v6.561a.649.649 0 01-.652.645.649.649 0 01-.653-.645V9.1a.762.762 0 00-.766-.758.762.762 0 00-.766.758v7.472a2.037 2.037 0 01-2.048 2.026 2.037 2.037 0 01-2.048-2.026v-12.5a.785.785 0 00-.788-.753.785.785 0 00-.789.752l-.001 15.904A2.037 2.037 0 0113.441 22a2.037 2.037 0 01-2.048-2.026V18.04c0-.356.292-.645.652-.645.36 0 .652.289.652.645v1.934c0 .263.142.506.372.638.23.131.514.131.744 0a.734.734 0 00.372-.638V4.07c0-1.143.937-2.07 2.093-2.07zm-5.674 0c1.156 0 2.093.927 2.093 2.07v11.523a.648.648 0 01-.652.645.648.648 0 01-.652-.645V4.07a.785.785 0 00-.789-.78.785.785 0 00-.789.78v14.013a2.06 2.06 0 01-2.07 2.048 2.06 2.06 0 01-2.071-2.048V9.1a.762.762 0 00-.766-.758.762.762 0 00-.766.758v3.8a2.06 2.06 0 01-2.071 2.049A2.06 2.06 0 010 12.9v-1.378c0-.357.292-.646.652-.646.36 0 .653.29.653.646V12.9c0 .418.343.757.766.757s.766-.339.766-.757V9.099a2.06 2.06 0 012.07-2.048 2.06 2.06 0 012.071 2.048v8.984c0 .419.343.758.767.758.423 0 .766-.339.766-.758V4.07c0-1.143.937-2.07 2.093-2.07z" />
</Svg>
);
}

View File

@@ -12,6 +12,7 @@ describe("resolveProviderIconName", () => {
expect(resolveProviderIconName("kiro")).toEqual({ kind: "builtin", id: "kiro" }); expect(resolveProviderIconName("kiro")).toEqual({ kind: "builtin", id: "kiro" });
expect(resolveProviderIconName("claude")).toEqual({ kind: "builtin", id: "claude" }); expect(resolveProviderIconName("claude")).toEqual({ kind: "builtin", id: "claude" });
expect(resolveProviderIconName("omp")).toEqual({ kind: "builtin", id: "omp" }); expect(resolveProviderIconName("omp")).toEqual({ kind: "builtin", id: "omp" });
expect(resolveProviderIconName("minimax")).toEqual({ kind: "builtin", id: "minimax" });
}); });
it("returns the catalog identifier for ACP catalog provider ids that ship an icon", () => { it("returns the catalog identifier for ACP catalog provider ids that ship an icon", () => {

View File

@@ -4,6 +4,7 @@ import { SvgXml } from "react-native-svg";
import { ClaudeIcon } from "@/components/icons/claude-icon"; import { ClaudeIcon } from "@/components/icons/claude-icon";
import { CodexIcon } from "@/components/icons/codex-icon"; import { CodexIcon } from "@/components/icons/codex-icon";
import { CopilotIcon } from "@/components/icons/copilot-icon"; import { CopilotIcon } from "@/components/icons/copilot-icon";
import { MiniMaxIcon } from "@/components/icons/minimax-icon";
import { OpenCodeIcon } from "@/components/icons/opencode-icon"; import { OpenCodeIcon } from "@/components/icons/opencode-icon";
import { OmpIcon } from "@/components/icons/omp-icon"; import { OmpIcon } from "@/components/icons/omp-icon";
import { PiIcon } from "@/components/icons/pi-icon"; import { PiIcon } from "@/components/icons/pi-icon";
@@ -22,6 +23,7 @@ const BUILTIN_PROVIDER_ICONS: Record<string, ProviderIconComponent> = {
codex: CodexIcon as unknown as ProviderIconComponent, codex: CodexIcon as unknown as ProviderIconComponent,
copilot: CopilotIcon as unknown as ProviderIconComponent, copilot: CopilotIcon as unknown as ProviderIconComponent,
kiro: PackagePlus, kiro: PackagePlus,
minimax: MiniMaxIcon as unknown as ProviderIconComponent,
omp: OmpIcon as unknown as ProviderIconComponent, omp: OmpIcon as unknown as ProviderIconComponent,
opencode: OpenCodeIcon as unknown as ProviderIconComponent, opencode: OpenCodeIcon as unknown as ProviderIconComponent,
pi: PiIcon as unknown as ProviderIconComponent, pi: PiIcon as unknown as ProviderIconComponent,

View File

@@ -3,6 +3,7 @@ export const BUILTIN_PROVIDER_ICON_NAMES = [
"codex", "codex",
"copilot", "copilot",
"kiro", "kiro",
"minimax",
"omp", "omp",
"opencode", "opencode",
"pi", "pi",

View File

@@ -9,6 +9,7 @@ import { CopilotQuotaProvider } from "./providers/copilot.js";
import { CursorQuotaProvider } from "./providers/cursor.js"; import { CursorQuotaProvider } from "./providers/cursor.js";
import { GrokQuotaProvider } from "./providers/grok.js"; import { GrokQuotaProvider } from "./providers/grok.js";
import { KimiQuotaProvider } from "./providers/kimi.js"; import { KimiQuotaProvider } from "./providers/kimi.js";
import { MiniMaxQuotaProvider } from "./providers/minimax.js";
import { ZaiQuotaProvider } from "./providers/zai.js"; import { ZaiQuotaProvider } from "./providers/zai.js";
export const PROVIDER_USAGE_FETCHERS: readonly ProviderUsageFetcherManifestEntry[] = [ export const PROVIDER_USAGE_FETCHERS: readonly ProviderUsageFetcherManifestEntry[] = [
@@ -48,6 +49,10 @@ export const PROVIDER_USAGE_FETCHERS: readonly ProviderUsageFetcherManifestEntry
providerId: "kimi", providerId: "kimi",
create: (options) => new KimiQuotaProvider({ logger: options.logger, fetch: options.fetch }), create: (options) => new KimiQuotaProvider({ logger: options.logger, fetch: options.fetch }),
}, },
{
providerId: "minimax",
create: (options) => new MiniMaxQuotaProvider({ logger: options.logger, fetch: options.fetch }),
},
]; ];
export function createProviderUsageFetchers( export function createProviderUsageFetchers(

View File

@@ -0,0 +1,275 @@
import { existsSync, promises as fs } from "node:fs";
import { homedir } from "node:os";
import { join } from "node:path";
import type { Logger } from "pino";
import { z } from "zod";
import type { ProviderUsage, ProviderUsageWindow } from "../../../server/messages.js";
import type { ProviderApiFetch, ProviderUsageFetcher } from "../provider.js";
import {
ApiNumberSchema,
ApiOptionalStringSchema,
fetchProviderApi,
unavailableUsage,
windowFromUsedPct,
} from "../usage.js";
const MINIMAX_GLOBAL_BASE_URL = "https://api.minimax.io";
const MINIMAX_CN_BASE_URL = "https://api.minimaxi.com";
const MINIMAX_CREDENTIALS_PATH = join(homedir(), ".mmx", "credentials.json");
const MINIMAX_CONFIG_PATH = join(homedir(), ".mmx", "config.json");
const MiniMaxModelRemainSchema = z.object({
model_name: ApiOptionalStringSchema,
start_time: ApiNumberSchema.optional(),
end_time: ApiNumberSchema.optional(),
remains_time: ApiNumberSchema.optional(),
current_interval_total_count: ApiNumberSchema.optional(),
current_interval_usage_count: ApiNumberSchema.optional(),
current_interval_remaining_percent: ApiNumberSchema.optional(),
current_weekly_total_count: ApiNumberSchema.optional(),
current_weekly_usage_count: ApiNumberSchema.optional(),
current_weekly_remaining_percent: ApiNumberSchema.optional(),
current_interval_status: ApiNumberSchema.optional(),
current_weekly_status: ApiNumberSchema.optional(),
weekly_start_time: ApiNumberSchema.optional(),
weekly_end_time: ApiNumberSchema.optional(),
weekly_remains_time: ApiNumberSchema.optional(),
weekly_boost_permille: ApiNumberSchema.optional(),
});
const MiniMaxQuotaResponseSchema = z.object({
model_remains: z.array(MiniMaxModelRemainSchema).optional(),
});
const MiniMaxCredentialsSchema = z.object({
access_token: z.string().optional(),
refresh_token: z.string().optional(),
expires_at: ApiOptionalStringSchema,
resource_url: ApiOptionalStringSchema,
});
const MiniMaxConfigSchema = z.object({
api_key: z.string().optional(),
region: z.string().optional(),
base_url: ApiOptionalStringSchema,
oauth: MiniMaxCredentialsSchema.optional(),
});
type MiniMaxModelRemain = z.infer<typeof MiniMaxModelRemainSchema>;
interface MiniMaxResolvedAuth {
token: string;
baseUrl: string;
}
interface MiniMaxQuotaProviderOptions {
logger: Logger;
fetch?: ProviderApiFetch;
configPath?: string;
credentialsPath?: string;
env?: NodeJS.ProcessEnv;
now?: () => number;
}
function resolveBaseUrl(input: { baseUrl?: string; region?: string }): string {
const explicit = input.baseUrl;
if (explicit && explicit.startsWith("http")) return explicit;
if (input.region === "cn") return MINIMAX_CN_BASE_URL;
return MINIMAX_GLOBAL_BASE_URL;
}
function computeUsedPct(
remaining: number | null | undefined,
total: number | null | undefined,
): number | null {
if (typeof remaining !== "number" || typeof total !== "number") return null;
if (!Number.isFinite(total) || total <= 0) return null;
if (!Number.isFinite(remaining)) return null;
const used = total - remaining;
return Math.max(0, Math.min(100, (used / total) * 100));
}
function epochMsToIso(value: number | null | undefined): string | null {
if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) return null;
return new Date(value).toISOString();
}
function toneForStatus(status: number | null | undefined): ProviderUsageWindow["tone"] {
if (status === 2) return "danger";
if (status === 3) return "default";
return "ok";
}
function toIntervalWindow(
modelName: string,
model: MiniMaxModelRemain,
): ProviderUsageWindow | null {
const total = model.current_interval_total_count ?? null;
const used = model.current_interval_usage_count ?? null;
const remainingPercent = model.current_interval_remaining_percent ?? null;
const usedPct =
typeof remainingPercent === "number" && Number.isFinite(remainingPercent)
? Math.max(0, Math.min(100, 100 - remainingPercent))
: computeUsedPct(
typeof total === "number" && typeof used === "number" ? total - used : null,
total,
);
if (usedPct === null) return null;
return windowFromUsedPct({
id: `interval_${modelName}`,
label: `${modelName} · Interval`,
utilizationPct: usedPct,
resetsAt: epochMsToIso(model.end_time),
tone: toneForStatus(model.current_interval_status),
});
}
function toWeeklyWindow(modelName: string, model: MiniMaxModelRemain): ProviderUsageWindow | null {
const total = model.current_weekly_total_count ?? null;
const used = model.current_weekly_usage_count ?? null;
const remainingPercent = model.current_weekly_remaining_percent ?? null;
let usedPct: number | null = null;
if (typeof remainingPercent === "number" && Number.isFinite(remainingPercent)) {
usedPct = Math.max(0, Math.min(100, 100 - remainingPercent));
} else if (typeof total === "number" && typeof used === "number") {
usedPct = computeUsedPct(total - used, total);
}
if (usedPct === null) return null;
return windowFromUsedPct({
id: `weekly_${modelName}`,
label: `${modelName} · Weekly`,
utilizationPct: usedPct,
resetsAt: epochMsToIso(model.weekly_end_time),
tone: toneForStatus(model.current_weekly_status),
});
}
export class MiniMaxQuotaProvider implements ProviderUsageFetcher {
readonly providerId = "minimax";
readonly displayName = "MiniMax";
private readonly logger: Logger;
private readonly fetchApi: ProviderApiFetch;
private readonly configPath: string;
private readonly credentialsPath: string;
private readonly env: NodeJS.ProcessEnv;
private readonly now: () => number;
constructor(options: MiniMaxQuotaProviderOptions) {
this.logger = options.logger;
this.fetchApi = options.fetch ?? fetch;
this.configPath = options.configPath ?? MINIMAX_CONFIG_PATH;
this.credentialsPath = options.credentialsPath ?? MINIMAX_CREDENTIALS_PATH;
this.env = options.env ?? process.env;
this.now = options.now ?? Date.now;
}
async fetchUsage(): Promise<ProviderUsage> {
const auth = await this.resolveAuth();
if (!auth) return unavailableUsage(this);
const res = await fetchProviderApi(this.fetchApi, `${auth.baseUrl}/v1/token_plan/remains`, {
headers: {
Authorization: `Bearer ${auth.token}`,
Accept: "application/json",
},
});
if (!res.ok) {
this.logger.debug({ status: res.status }, "MiniMax usage fetch failed");
return unavailableUsage(this);
}
const resp = MiniMaxQuotaResponseSchema.parse(await res.json());
const models = resp.model_remains ?? [];
const windows: ProviderUsageWindow[] = [];
for (const model of models) {
const name = model.model_name ?? "token-plan";
const intervalWindow = toIntervalWindow(name, model);
if (intervalWindow) windows.push(intervalWindow);
const weeklyWindow = toWeeklyWindow(name, model);
if (weeklyWindow) windows.push(weeklyWindow);
}
return {
providerId: this.providerId,
displayName: this.displayName,
status: windows.length > 0 ? "available" : "unavailable",
planLabel: null,
windows,
balances: [],
details: [],
error: null,
};
}
private async resolveAuth(): Promise<MiniMaxResolvedAuth | null> {
const envToken = this.env["MINIMAX_API_KEY"];
if (envToken) {
const envBase = this.env["MINIMAX_BASE_URL"];
return {
token: envToken,
baseUrl: resolveBaseUrl({ baseUrl: envBase }),
};
}
const credentials = await this.readCredentials();
if (credentials?.access_token && !this.isExpired(credentials.expires_at)) {
return {
token: credentials.access_token,
baseUrl: resolveBaseUrl({ baseUrl: credentials.resource_url }),
};
}
const config = await this.readConfig();
if (config?.api_key) {
return {
token: config.api_key,
baseUrl: resolveBaseUrl({
baseUrl: config.base_url,
region: config.region,
}),
};
}
if (config?.oauth?.access_token && !this.isExpired(config.oauth.expires_at)) {
return {
token: config.oauth.access_token,
baseUrl: resolveBaseUrl({
baseUrl: config.oauth.resource_url ?? config.base_url,
region: config.region,
}),
};
}
return null;
}
private isExpired(expiresAt: string | null | undefined): boolean {
if (!expiresAt) return false;
const parsed = Date.parse(expiresAt);
if (!Number.isFinite(parsed)) return false;
return parsed <= this.now();
}
private async readCredentials(): Promise<z.infer<typeof MiniMaxCredentialsSchema> | null> {
if (!existsSync(this.credentialsPath)) return null;
try {
const raw = JSON.parse(await fs.readFile(this.credentialsPath, "utf8"));
return MiniMaxCredentialsSchema.parse(raw);
} catch {
return null;
}
}
private async readConfig(): Promise<z.infer<typeof MiniMaxConfigSchema> | null> {
if (!existsSync(this.configPath)) return null;
try {
const raw = JSON.parse(await fs.readFile(this.configPath, "utf8"));
return MiniMaxConfigSchema.parse(raw);
} catch {
return null;
}
}
}

View File

@@ -11,6 +11,7 @@ import { CopilotQuotaProvider } from "./providers/copilot.js";
import { CursorQuotaProvider } from "./providers/cursor.js"; import { CursorQuotaProvider } from "./providers/cursor.js";
import { GrokQuotaProvider } from "./providers/grok.js"; import { GrokQuotaProvider } from "./providers/grok.js";
import { KimiQuotaProvider } from "./providers/kimi.js"; import { KimiQuotaProvider } from "./providers/kimi.js";
import { MiniMaxQuotaProvider } from "./providers/minimax.js";
import { ZaiQuotaProvider } from "./providers/zai.js"; import { ZaiQuotaProvider } from "./providers/zai.js";
import { ProviderUsageService } from "./service.js"; import { ProviderUsageService } from "./service.js";
@@ -50,6 +51,24 @@ function writeKimiCredentials(dir: string, accessToken: string): void {
); );
} }
function writeMiniMaxConfig(dir: string, payload: Record<string, unknown>): void {
mkdirSync(join(dir, ".mmx"), { recursive: true });
writeFileSync(join(dir, ".mmx", "config.json"), JSON.stringify(payload));
}
function writeMiniMaxCredentials(
dir: string,
accessToken: string,
expiresAt?: string,
resourceUrl?: string,
): void {
mkdirSync(join(dir, ".mmx"), { recursive: true });
const payload: Record<string, unknown> = { access_token: accessToken };
if (expiresAt !== undefined) payload["expires_at"] = expiresAt;
if (resourceUrl !== undefined) payload["resource_url"] = resourceUrl;
writeFileSync(join(dir, ".mmx", "credentials.json"), JSON.stringify(payload));
}
function makeClaudeResponse( function makeClaudeResponse(
overrides: Partial<{ overrides: Partial<{
five_hour: { utilization: number | string; resets_at: string }; five_hour: { utilization: number | string; resets_at: string };
@@ -315,6 +334,8 @@ describe("real provider usage fetchers", () => {
"KIMI_API_KEY", "KIMI_API_KEY",
"KIMI_CODE_HOME", "KIMI_CODE_HOME",
"CODEX_HOME", "CODEX_HOME",
"MINIMAX_API_KEY",
"MINIMAX_BASE_URL",
]) { ]) {
delete process.env[key]; delete process.env[key];
} }
@@ -339,6 +360,8 @@ describe("real provider usage fetchers", () => {
platform?: typeof process.platform; platform?: typeof process.platform;
keychain?: () => Promise<unknown | null>; keychain?: () => Promise<unknown | null>;
kimiHomeDir?: string; kimiHomeDir?: string;
miniMaxConfigPath?: string;
miniMaxCredentialsPath?: string;
} = {}, } = {},
) { ) {
const logger = createLogger(); const logger = createLogger();
@@ -365,6 +388,13 @@ describe("real provider usage fetchers", () => {
fetch: fetchThroughTestDouble, fetch: fetchThroughTestDouble,
homeDir: options.kimiHomeDir, homeDir: options.kimiHomeDir,
}), }),
new MiniMaxQuotaProvider({
logger,
fetch: fetchThroughTestDouble,
configPath: options.miniMaxConfigPath ?? join(homeDir, ".mmx", "config.json"),
credentialsPath:
options.miniMaxCredentialsPath ?? join(homeDir, ".mmx", "credentials.json"),
}),
], ],
cacheTtlMs: 0, cacheTtlMs: 0,
}); });
@@ -750,4 +780,143 @@ describe("real provider usage fetchers", () => {
], ],
}); });
}); });
it("fetches MiniMax usage from MINIMAX_API_KEY against the global endpoint", async () => {
process.env["MINIMAX_API_KEY"] = "minimax_test_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: "MiniMax-M2.7",
end_time: Date.parse("2026-06-19T05:00:00.000Z"),
weekly_end_time: Date.parse("2026-06-26T00:00:00.000Z"),
current_interval_total_count: 1000,
current_interval_usage_count: 250,
current_interval_remaining_percent: 75,
current_weekly_total_count: 5000,
current_weekly_usage_count: 1200,
current_weekly_remaining_percent: 76,
},
],
});
}) as unknown as typeof fetch;
const miniMax = findProvider(await service().listUsage(), "minimax");
expect(requestedUrl).toBe("https://api.minimax.io/v1/token_plan/remains");
expect(authorization).toBe("Bearer minimax_test_token");
expect(miniMax).toMatchObject({
status: "available",
windows: expect.arrayContaining([
expect.objectContaining({
id: "interval_MiniMax-M2.7",
label: "MiniMax-M2.7 · Interval",
usedPct: 25,
remainingPct: 75,
resetsAt: "2026-06-19T05:00:00.000Z",
}),
expect.objectContaining({
id: "weekly_MiniMax-M2.7",
label: "MiniMax-M2.7 · Weekly",
usedPct: 24,
remainingPct: 76,
resetsAt: "2026-06-26T00:00:00.000Z",
}),
]),
});
});
it("returns unavailable MiniMax usage when no credentials are configured", async () => {
fetchApi = vi.fn() as never;
const miniMax = findProvider(await service().listUsage(), "minimax");
expect(miniMax.status).toBe("unavailable");
expect(fetchApi).not.toHaveBeenCalled();
});
it("reads MiniMax OAuth credentials from the CLI credentials file", async () => {
writeMiniMaxCredentials(
homeDir,
"minimax_oauth_token",
"2030-01-01T00:00:00.000Z",
"https://account.example.com",
);
let requestedUrl: string | null = null;
fetchApi = (async (url: RequestInfo | URL) => {
requestedUrl = url.toString();
return jsonResponse({ model_remains: [] });
}) as unknown as typeof fetch;
await service().listUsage();
expect(requestedUrl).toBe("https://account.example.com/v1/token_plan/remains");
});
it("falls back to MiniMax api_key in the CLI config file", async () => {
writeMiniMaxConfig(homeDir, {
api_key: "minimax_config_key",
region: "cn",
});
let requestedUrl: string | null = null;
fetchApi = (async (url: RequestInfo | URL) => {
requestedUrl = url.toString();
return jsonResponse({ model_remains: [] });
}) as unknown as typeof fetch;
const miniMax = findProvider(await service().listUsage(), "minimax");
expect(requestedUrl).toBe("https://api.minimaxi.com/v1/token_plan/remains");
expect(miniMax.status).toBe("unavailable");
});
it("marks exhausted MiniMax interval windows with a danger tone", async () => {
process.env["MINIMAX_API_KEY"] = "minimax_test_token";
fetchApi = mockFetch(
new Map([
[
"https://api.minimax.io/v1/token_plan/remains",
() =>
jsonResponse({
model_remains: [
{
model_name: "MiniMax-M2.7",
end_time: Date.parse("2026-06-19T05:00:00.000Z"),
weekly_end_time: Date.parse("2026-06-26T00:00:00.000Z"),
current_interval_total_count: 100,
current_interval_usage_count: 100,
current_interval_remaining_percent: 0,
current_interval_status: 2,
current_weekly_total_count: 100,
current_weekly_usage_count: 10,
current_weekly_remaining_percent: 90,
current_weekly_status: 1,
},
],
}),
],
]),
);
const miniMax = findProvider(await service().listUsage(), "minimax");
expect(miniMax).toMatchObject({
status: "available",
windows: expect.arrayContaining([
expect.objectContaining({
id: "interval_MiniMax-M2.7",
usedPct: 100,
tone: "danger",
}),
expect.objectContaining({
id: "weekly_MiniMax-M2.7",
tone: "ok",
}),
]),
});
});
}); });