mirror of
https://github.com/getpaseo/paseo.git
synced 2026-10-31 21:52:54 +00:00
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.
108 lines
3.2 KiB
TypeScript
108 lines
3.2 KiB
TypeScript
import type { UsageInput } from "../shared/input.js";
|
|
import { promises as fs } from "node:fs";
|
|
import { homedir } from "node:os";
|
|
import { join } from "node:path";
|
|
import { z } from "zod";
|
|
import {
|
|
unavailable,
|
|
type UsageAccount,
|
|
type UsageReport,
|
|
type UsageDetail,
|
|
} from "@getpaseo/plugin/server/usage";
|
|
|
|
const ApiOptionalStringSchema = z.preprocess(
|
|
(value) => (value == null ? undefined : value),
|
|
z.coerce.string().optional(),
|
|
);
|
|
|
|
const ZaiUsageResponseSchema = z.object({
|
|
data: z
|
|
.array(
|
|
z.object({
|
|
productName: ApiOptionalStringSchema,
|
|
status: ApiOptionalStringSchema,
|
|
purchaseTime: ApiOptionalStringSchema,
|
|
valid: ApiOptionalStringSchema,
|
|
}),
|
|
)
|
|
.optional(),
|
|
});
|
|
|
|
/** The credentials file the `glm-acp-agent` CLI writes. */
|
|
const ZaiCredentialsSchema = z.object({
|
|
z_ai_api_key: z.string().optional(),
|
|
});
|
|
|
|
export async function fetchUsage(
|
|
input: UsageInput,
|
|
fetchApi: typeof fetch = fetch,
|
|
): Promise<UsageReport> {
|
|
const token = await readToken(input);
|
|
if (!token) throw new Error("Z.ai login store no longer exists");
|
|
|
|
const res = await fetchApi("https://api.z.ai/api/biz/subscription/list", {
|
|
signal: AbortSignal.timeout(15_000),
|
|
headers: {
|
|
Authorization: `Bearer ${token}`,
|
|
Accept: "application/json",
|
|
},
|
|
});
|
|
|
|
if (res.status === 401 || res.status === 403)
|
|
return unavailable({ kind: "rejected", status: res.status });
|
|
if (!res.ok) throw new Error(`Z.ai usage API returned ${res.status}`);
|
|
|
|
const resp = ZaiUsageResponseSchema.parse(await res.json());
|
|
const sub = resp.data?.[0];
|
|
if (!sub) return unavailable({ kind: "no_quota", detail: "No active coding plan" });
|
|
|
|
const details: UsageDetail[] = [];
|
|
if (sub.status) details.push({ id: "status", label: "Status", value: sub.status });
|
|
if (sub.valid) details.push({ id: "valid", label: "Valid", value: sub.valid });
|
|
if (sub.purchaseTime) {
|
|
details.push({ id: "purchase_time", label: "Purchased", value: sub.purchaseTime });
|
|
}
|
|
|
|
return {
|
|
status: "available",
|
|
planLabel: sub.productName || undefined,
|
|
windows: [],
|
|
balances: [],
|
|
details,
|
|
};
|
|
}
|
|
|
|
/**
|
|
* The Z.AI API key comes from the environment or from the credentials file the
|
|
* `glm-acp-agent` CLI writes. The CLI names the variable `Z_AI_API_KEY`; the
|
|
* older `ZAI_API_KEY` spelling is still accepted.
|
|
*/
|
|
async function readToken(input: UsageInput): Promise<string | undefined> {
|
|
if (input.store === "env") {
|
|
return process.env[input.locator];
|
|
}
|
|
try {
|
|
const raw: unknown = JSON.parse(await fs.readFile(input.locator, "utf8"));
|
|
return ZaiCredentialsSchema.parse(raw).z_ai_api_key;
|
|
} catch {
|
|
return undefined;
|
|
}
|
|
}
|
|
|
|
export async function discover(): Promise<UsageAccount[]> {
|
|
for (const locator of ["Z_AI_API_KEY", "ZAI_API_KEY", "GLM_API_KEY"]) {
|
|
if (process.env[locator]) return [{ key: "default", input: { store: "env", locator } }];
|
|
}
|
|
|
|
const credentialsPath = join(
|
|
process.env.XDG_CONFIG_HOME || join(homedir(), ".config"),
|
|
"glm-acp-agent",
|
|
"credentials.json",
|
|
);
|
|
if (await readToken({ store: "file", locator: credentialsPath })) {
|
|
return [{ key: "default", input: { store: "file", locator: credentialsPath } }];
|
|
}
|
|
|
|
return [];
|
|
}
|