@getpaseo/server 0.11.0-beta.2 → 0.11.0-beta.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/server/builtin-plugins/claude-usage-source/index.server.ts +1 -2
- package/dist/server/builtin-plugins/claude-usage-source/server/stores.ts +0 -1
- package/dist/server/builtin-plugins/claude-usage-source/server/usage.ts +72 -36
- package/dist/server/builtin-plugins/codex-usage-source/index.server.ts +1 -2
- package/dist/server/builtin-plugins/codex-usage-source/server/stores.ts +0 -1
- package/dist/server/builtin-plugins/codex-usage-source/server/usage.ts +22 -12
- package/dist/server/builtin-plugins/copilot-usage-source/index.server.ts +2 -3
- package/dist/server/builtin-plugins/copilot-usage-source/server/usage.ts +28 -39
- package/dist/server/builtin-plugins/copilot-usage-source/shared/input.ts +6 -2
- package/dist/server/builtin-plugins/cursor-usage-source/index.server.ts +2 -3
- package/dist/server/builtin-plugins/cursor-usage-source/server/usage.ts +53 -54
- package/dist/server/builtin-plugins/cursor-usage-source/shared/input.ts +6 -2
- package/dist/server/builtin-plugins/grok-usage-source/index.server.ts +2 -3
- package/dist/server/builtin-plugins/grok-usage-source/server/usage.ts +23 -27
- package/dist/server/builtin-plugins/grok-usage-source/shared/input.ts +6 -2
- package/dist/server/builtin-plugins/kimi-usage-source/index.server.ts +2 -3
- package/dist/server/builtin-plugins/kimi-usage-source/server/usage.ts +44 -67
- package/dist/server/builtin-plugins/kimi-usage-source/shared/input.ts +6 -2
- package/dist/server/builtin-plugins/minimax-usage-source/index.server.ts +2 -3
- package/dist/server/builtin-plugins/minimax-usage-source/server/usage.ts +64 -100
- package/dist/server/builtin-plugins/minimax-usage-source/shared/input.ts +6 -2
- package/dist/server/builtin-plugins/muse-provider/server/usage.ts +9 -9
- package/dist/server/builtin-plugins/opencode-go-usage-source/index.server.ts +1 -2
- package/dist/server/builtin-plugins/opencode-go-usage-source/server/usage.ts +8 -12
- package/dist/server/builtin-plugins/opencode-go-usage-source/shared/input.ts +1 -2
- package/dist/server/builtin-plugins/package.json +1 -1
- package/dist/server/builtin-plugins/zai-usage-source/index.server.ts +2 -3
- package/dist/server/builtin-plugins/zai-usage-source/server/usage.ts +13 -11
- package/dist/server/builtin-plugins/zai-usage-source/shared/input.ts +6 -2
- package/dist/server/server/plugins/index.d.ts +18 -3
- package/dist/server/server/plugins/index.js +1 -2
- package/dist/server/server/plugins/plugin-process-protocol.d.ts +0 -5
- package/dist/server/server/plugins/plugin-process-protocol.js +0 -8
- package/dist/server/server/plugins/plugin-process.js +2 -7
- package/dist/server/server/plugins/runtime.d.ts +0 -1
- package/dist/server/server/plugins/runtime.js +0 -11
- package/dist/server/server/plugins/test-fixtures/usage-source/index.server.js +11 -2
- package/dist/server/server/plugins/test-fixtures/usage-source-directory/index.server.js +11 -2
- package/dist/server/server/plugins/usage-sources/index.d.ts +6 -8
- package/dist/server/server/plugins/usage-sources/index.js +79 -84
- package/dist/server/web-ui/_expo/static/js/web/{index-a43718759ebeb27df202e8b9fc2fe58a.js → index-1a3199cb049d45e47bfd4c216ce98838.js} +8 -8
- package/dist/server/web-ui/_expo/static/js/web/index-1a3199cb049d45e47bfd4c216ce98838.js.br +0 -0
- package/dist/server/web-ui/_expo/static/js/web/{index-a43718759ebeb27df202e8b9fc2fe58a.js.gz → index-1a3199cb049d45e47bfd4c216ce98838.js.gz} +0 -0
- package/dist/server/web-ui/index.html +1 -1
- package/dist/server/web-ui/index.html.br +0 -0
- package/dist/server/web-ui/index.html.gz +0 -0
- package/package.json +6 -6
- package/dist/server/web-ui/_expo/static/js/web/index-a43718759ebeb27df202e8b9fc2fe58a.js.br +0 -0
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import type { UsageInput } from "../shared/input.js";
|
|
2
|
-
import {
|
|
2
|
+
import { promises as fs } from "node:fs";
|
|
3
3
|
import { homedir } from "node:os";
|
|
4
4
|
import { join } from "node:path";
|
|
5
5
|
import { z } from "zod";
|
|
6
6
|
import {
|
|
7
7
|
toneFromUsedPct,
|
|
8
8
|
usedPctOf,
|
|
9
|
-
|
|
9
|
+
unavailable,
|
|
10
|
+
type UsageAccount,
|
|
10
11
|
windowFromUsedPct,
|
|
11
12
|
type UsageReport,
|
|
12
13
|
type UsageWindow,
|
|
@@ -109,23 +110,8 @@ export async function fetchUsage(
|
|
|
109
110
|
input: UsageInput,
|
|
110
111
|
fetchApi: typeof fetch = fetch,
|
|
111
112
|
): Promise<UsageReport> {
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
async function readGrokToken(): Promise<string | null> {
|
|
116
|
-
// homeDir override is for tests: Windows os.homedir() ignores $HOME (uses USERPROFILE).
|
|
117
|
-
const path = join(homeDir ?? homedir(), ".grok", "auth.json");
|
|
118
|
-
if (!existsSync(path)) return null;
|
|
119
|
-
try {
|
|
120
|
-
return extractGrokTokenFromAuth(JSON.parse(await fs.readFile(path, "utf8")));
|
|
121
|
-
} catch {
|
|
122
|
-
return null;
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
const token = process.env["GROK_API_KEY"] || process.env["GROK_TOKEN"] || (await readGrokToken());
|
|
127
|
-
|
|
128
|
-
if (!token) return unavailableUsage();
|
|
113
|
+
const token = await readToken(input);
|
|
114
|
+
if (!token) throw new Error("Grok login store no longer exists");
|
|
129
115
|
|
|
130
116
|
// The Grok CLI's /usage uses ?format=credits; without it, unified-billing accounts
|
|
131
117
|
// get a zeroed legacy monthly shape (monthlyLimit.val 0) instead of real usage.
|
|
@@ -138,9 +124,9 @@ export async function fetchUsage(
|
|
|
138
124
|
},
|
|
139
125
|
});
|
|
140
126
|
|
|
141
|
-
if (
|
|
142
|
-
return
|
|
143
|
-
}
|
|
127
|
+
if (res.status === 401 || res.status === 403)
|
|
128
|
+
return unavailable({ kind: "rejected", status: res.status });
|
|
129
|
+
if (!res.ok) throw new Error(`Grok usage API returned ${res.status}`);
|
|
144
130
|
|
|
145
131
|
const resp = GrokUsageResponseSchema.parse(await res.json());
|
|
146
132
|
const balance = grokMonthlyCreditBalance(resp);
|
|
@@ -155,12 +141,22 @@ export async function fetchUsage(
|
|
|
155
141
|
};
|
|
156
142
|
}
|
|
157
143
|
|
|
158
|
-
|
|
159
|
-
if (
|
|
144
|
+
async function readToken(input: UsageInput): Promise<string | undefined> {
|
|
145
|
+
if (input.store === "env") return process.env[input.locator];
|
|
160
146
|
try {
|
|
161
|
-
|
|
162
|
-
|
|
147
|
+
return (
|
|
148
|
+
extractGrokTokenFromAuth(JSON.parse(await fs.readFile(input.locator, "utf8"))) ?? undefined
|
|
149
|
+
);
|
|
163
150
|
} catch {
|
|
164
|
-
return
|
|
151
|
+
return undefined;
|
|
165
152
|
}
|
|
166
153
|
}
|
|
154
|
+
export async function discover(): Promise<UsageAccount[]> {
|
|
155
|
+
const candidates: UsageInput[] = ["GROK_API_KEY", "GROK_TOKEN"].map((locator) => ({
|
|
156
|
+
store: "env",
|
|
157
|
+
locator,
|
|
158
|
+
}));
|
|
159
|
+
candidates.push({ store: "file", locator: join(homedir(), ".grok", "auth.json") });
|
|
160
|
+
for (const input of candidates) if (await readToken(input)) return [{ key: "default", input }];
|
|
161
|
+
return [];
|
|
162
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { PluginServerContext } from "@getpaseo/plugin/server";
|
|
2
2
|
import { inputSchema } from "./shared/input.js";
|
|
3
|
-
import { fetchUsage,
|
|
3
|
+
import { fetchUsage, discover } from "./server/usage.js";
|
|
4
4
|
|
|
5
5
|
export default function contribute(server: PluginServerContext) {
|
|
6
6
|
server.registerUsageSource({
|
|
@@ -8,8 +8,7 @@ export default function contribute(server: PluginServerContext) {
|
|
|
8
8
|
label: "Kimi",
|
|
9
9
|
icon: "icon.svg",
|
|
10
10
|
input: inputSchema,
|
|
11
|
-
discover:
|
|
12
|
-
identify,
|
|
11
|
+
discover: () => discover(),
|
|
13
12
|
fetch: fetchUsage,
|
|
14
13
|
});
|
|
15
14
|
return () => {};
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import type { UsageInput } from "../shared/input.js";
|
|
2
|
-
import {
|
|
2
|
+
import { promises as fs } from "node:fs";
|
|
3
3
|
import { homedir } from "node:os";
|
|
4
4
|
import { join } from "node:path";
|
|
5
5
|
import { z } from "zod";
|
|
6
6
|
import {
|
|
7
7
|
toneFromUsedPct,
|
|
8
|
-
|
|
8
|
+
unavailable,
|
|
9
|
+
type UsageAccount,
|
|
9
10
|
windowFromUsedPct,
|
|
10
11
|
type UsageReport,
|
|
11
12
|
type UsageWindow,
|
|
@@ -251,9 +252,6 @@ export async function fetchUsage(
|
|
|
251
252
|
input: UsageInput,
|
|
252
253
|
fetchApi: typeof fetch = fetch,
|
|
253
254
|
): Promise<UsageReport> {
|
|
254
|
-
void input;
|
|
255
|
-
const homeDir = homedir();
|
|
256
|
-
|
|
257
255
|
async function callUsageApi(token: string): Promise<Response> {
|
|
258
256
|
return fetchApi(KIMI_USAGE_URL, {
|
|
259
257
|
signal: AbortSignal.timeout(15_000),
|
|
@@ -264,52 +262,19 @@ export async function fetchUsage(
|
|
|
264
262
|
});
|
|
265
263
|
}
|
|
266
264
|
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
const credentials = await readCredentialFile(path);
|
|
275
|
-
if (credentials?.access_token) {
|
|
276
|
-
return { ...credentials, access_token: credentials.access_token };
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
return null;
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
function credentialPaths(): string[] {
|
|
283
|
-
const home = homeDir;
|
|
284
|
-
return [
|
|
285
|
-
join(
|
|
286
|
-
process.env["KIMI_CODE_HOME"] || join(home, ".kimi-code"),
|
|
287
|
-
"credentials",
|
|
288
|
-
"kimi-code.json",
|
|
289
|
-
),
|
|
290
|
-
join(home, ".kimi", "credentials", "kimi-code.json"),
|
|
291
|
-
];
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
async function readCredentialFile(path: string): Promise<KimiAuth | null> {
|
|
295
|
-
if (!existsSync(path)) return null;
|
|
296
|
-
try {
|
|
297
|
-
return KimiAuthSchema.parse(JSON.parse(await fs.readFile(path, "utf8")));
|
|
298
|
-
} catch {
|
|
299
|
-
return null;
|
|
300
|
-
}
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
const credentials = await readCredentials();
|
|
304
|
-
if (!credentials) return unavailableUsage();
|
|
265
|
+
const credentials = await readCredentials(input);
|
|
266
|
+
if (!credentials) throw new Error("Kimi login store no longer exists");
|
|
267
|
+
if (credentials.expires_at != null && credentials.expires_at * 1000 <= Date.now())
|
|
268
|
+
return unavailable({
|
|
269
|
+
kind: "expired",
|
|
270
|
+
expiresAt: new Date(credentials.expires_at * 1000).toISOString(),
|
|
271
|
+
});
|
|
305
272
|
|
|
306
273
|
const res = await callUsageApi(credentials.access_token);
|
|
307
274
|
|
|
308
|
-
if (
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
return unavailableUsage();
|
|
312
|
-
}
|
|
275
|
+
if (res.status === 401 || res.status === 403)
|
|
276
|
+
return unavailable({ kind: "rejected", status: res.status });
|
|
277
|
+
if (!res.ok) throw new Error(`Kimi usage API returned ${res.status}`);
|
|
313
278
|
|
|
314
279
|
const windows = kimiUsageWindowsFromPayload(await res.json());
|
|
315
280
|
|
|
@@ -322,24 +287,36 @@ export async function fetchUsage(
|
|
|
322
287
|
};
|
|
323
288
|
}
|
|
324
289
|
|
|
325
|
-
|
|
326
|
-
if (
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
join(
|
|
330
|
-
process.env["KIMI_CODE_HOME"] || join(home, ".kimi-code"),
|
|
331
|
-
"credentials",
|
|
332
|
-
"kimi-code.json",
|
|
333
|
-
),
|
|
334
|
-
join(home, ".kimi", "credentials", "kimi-code.json"),
|
|
335
|
-
];
|
|
336
|
-
for (const path of paths) {
|
|
337
|
-
try {
|
|
338
|
-
const auth = KimiAuthSchema.parse(JSON.parse(await fs.readFile(path, "utf8")));
|
|
339
|
-
if (auth.access_token) return { key: "default" };
|
|
340
|
-
} catch {
|
|
341
|
-
continue;
|
|
342
|
-
}
|
|
290
|
+
async function readCredentials(input: UsageInput): Promise<KimiCredentials | undefined> {
|
|
291
|
+
if (input.store === "env") {
|
|
292
|
+
const token = process.env[input.locator];
|
|
293
|
+
return token ? { access_token: token } : undefined;
|
|
343
294
|
}
|
|
344
|
-
|
|
295
|
+
try {
|
|
296
|
+
const credentials = KimiAuthSchema.parse(JSON.parse(await fs.readFile(input.locator, "utf8")));
|
|
297
|
+
return credentials.access_token
|
|
298
|
+
? { ...credentials, access_token: credentials.access_token }
|
|
299
|
+
: undefined;
|
|
300
|
+
} catch {
|
|
301
|
+
return undefined;
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
export async function discover(): Promise<UsageAccount[]> {
|
|
305
|
+
const candidates: UsageInput[] = ["KIMI_TOKEN", "KIMI_API_KEY"].map((locator) => ({
|
|
306
|
+
store: "env",
|
|
307
|
+
locator,
|
|
308
|
+
}));
|
|
309
|
+
candidates.push(
|
|
310
|
+
...[
|
|
311
|
+
join(
|
|
312
|
+
process.env.KIMI_CODE_HOME || join(homedir(), ".kimi-code"),
|
|
313
|
+
"credentials",
|
|
314
|
+
"kimi-code.json",
|
|
315
|
+
),
|
|
316
|
+
join(homedir(), ".kimi", "credentials", "kimi-code.json"),
|
|
317
|
+
].map((locator) => ({ store: "file" as const, locator })),
|
|
318
|
+
);
|
|
319
|
+
for (const input of candidates)
|
|
320
|
+
if (await readCredentials(input)) return [{ key: "default", input }];
|
|
321
|
+
return [];
|
|
345
322
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { PluginServerContext } from "@getpaseo/plugin/server";
|
|
2
2
|
import { inputSchema } from "./shared/input.js";
|
|
3
|
-
import { fetchUsage,
|
|
3
|
+
import { fetchUsage, discover } from "./server/usage.js";
|
|
4
4
|
|
|
5
5
|
export default function contribute(server: PluginServerContext) {
|
|
6
6
|
server.registerUsageSource({
|
|
@@ -8,8 +8,7 @@ export default function contribute(server: PluginServerContext) {
|
|
|
8
8
|
label: "MiniMax",
|
|
9
9
|
icon: "icon.svg",
|
|
10
10
|
input: inputSchema,
|
|
11
|
-
discover:
|
|
12
|
-
identify,
|
|
11
|
+
discover: () => discover(),
|
|
13
12
|
fetch: fetchUsage,
|
|
14
13
|
});
|
|
15
14
|
return () => {};
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import type { UsageInput } from "../shared/input.js";
|
|
2
|
-
import {
|
|
2
|
+
import { promises as fs } from "node:fs";
|
|
3
3
|
import { homedir } from "node:os";
|
|
4
4
|
import { join } from "node:path";
|
|
5
5
|
import { z } from "zod";
|
|
6
6
|
import {
|
|
7
|
-
|
|
7
|
+
unavailable,
|
|
8
|
+
type UsageAccount,
|
|
8
9
|
windowFromUsedPct,
|
|
9
10
|
type UsageReport,
|
|
10
11
|
type UsageWindow,
|
|
@@ -148,83 +149,10 @@ export async function fetchUsage(
|
|
|
148
149
|
input: UsageInput,
|
|
149
150
|
fetchApi: typeof fetch = fetch,
|
|
150
151
|
): Promise<UsageReport> {
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
const now = Date.now;
|
|
156
|
-
|
|
157
|
-
async function resolveAuth(): Promise<MiniMaxResolvedAuth | null> {
|
|
158
|
-
const envToken = env["MINIMAX_API_KEY"];
|
|
159
|
-
if (envToken) {
|
|
160
|
-
const envBase = env["MINIMAX_BASE_URL"];
|
|
161
|
-
return {
|
|
162
|
-
token: envToken,
|
|
163
|
-
baseUrl: resolveBaseUrl({ baseUrl: envBase }),
|
|
164
|
-
};
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
const credentials = await readCredentials();
|
|
168
|
-
if (credentials?.access_token && !isExpired(credentials.expires_at)) {
|
|
169
|
-
return {
|
|
170
|
-
token: credentials.access_token,
|
|
171
|
-
baseUrl: resolveBaseUrl({ baseUrl: credentials.resource_url }),
|
|
172
|
-
};
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
const config = await readConfig();
|
|
176
|
-
if (config?.api_key) {
|
|
177
|
-
return {
|
|
178
|
-
token: config.api_key,
|
|
179
|
-
baseUrl: resolveBaseUrl({
|
|
180
|
-
baseUrl: config.base_url,
|
|
181
|
-
region: config.region,
|
|
182
|
-
}),
|
|
183
|
-
};
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
if (config?.oauth?.access_token && !isExpired(config.oauth.expires_at)) {
|
|
187
|
-
return {
|
|
188
|
-
token: config.oauth.access_token,
|
|
189
|
-
baseUrl: resolveBaseUrl({
|
|
190
|
-
baseUrl: config.oauth.resource_url ?? config.base_url,
|
|
191
|
-
region: config.region,
|
|
192
|
-
}),
|
|
193
|
-
};
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
return null;
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
function isExpired(expiresAt: string | null | undefined): boolean {
|
|
200
|
-
if (!expiresAt) return false;
|
|
201
|
-
const parsed = Date.parse(expiresAt);
|
|
202
|
-
if (!Number.isFinite(parsed)) return false;
|
|
203
|
-
return parsed <= now();
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
async function readCredentials(): Promise<z.infer<typeof MiniMaxCredentialsSchema> | null> {
|
|
207
|
-
if (!existsSync(credentialsPath)) return null;
|
|
208
|
-
try {
|
|
209
|
-
const raw = JSON.parse(await fs.readFile(credentialsPath, "utf8"));
|
|
210
|
-
return MiniMaxCredentialsSchema.parse(raw);
|
|
211
|
-
} catch {
|
|
212
|
-
return null;
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
async function readConfig(): Promise<z.infer<typeof MiniMaxConfigSchema> | null> {
|
|
217
|
-
if (!existsSync(configPath)) return null;
|
|
218
|
-
try {
|
|
219
|
-
const raw = JSON.parse(await fs.readFile(configPath, "utf8"));
|
|
220
|
-
return MiniMaxConfigSchema.parse(raw);
|
|
221
|
-
} catch {
|
|
222
|
-
return null;
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
const auth = await resolveAuth();
|
|
227
|
-
if (!auth) return unavailableUsage();
|
|
152
|
+
const auth = await readAuth(input);
|
|
153
|
+
if (!auth) throw new Error("MiniMax login store no longer exists");
|
|
154
|
+
if (auth.expiresAt && Date.parse(auth.expiresAt) <= Date.now())
|
|
155
|
+
return unavailable({ kind: "expired", expiresAt: new Date(auth.expiresAt).toISOString() });
|
|
228
156
|
|
|
229
157
|
const res = await fetchApi(`${auth.baseUrl}/v1/token_plan/remains`, {
|
|
230
158
|
signal: AbortSignal.timeout(15_000),
|
|
@@ -234,15 +162,18 @@ export async function fetchUsage(
|
|
|
234
162
|
},
|
|
235
163
|
});
|
|
236
164
|
|
|
237
|
-
if (
|
|
238
|
-
return
|
|
239
|
-
}
|
|
165
|
+
if (res.status === 401 || res.status === 403)
|
|
166
|
+
return unavailable({ kind: "rejected", status: res.status });
|
|
167
|
+
if (!res.ok) throw new Error(`MiniMax usage API returned ${res.status}`);
|
|
240
168
|
|
|
241
169
|
const resp = MiniMaxQuotaResponseSchema.parse(await res.json());
|
|
242
170
|
|
|
243
171
|
const statusCode = resp.base_resp?.status_code;
|
|
244
172
|
if (typeof statusCode === "number" && statusCode !== 0) {
|
|
245
|
-
return
|
|
173
|
+
return unavailable({
|
|
174
|
+
kind: "no_quota",
|
|
175
|
+
detail: resp.base_resp?.status_msg ?? "No active coding plan",
|
|
176
|
+
});
|
|
246
177
|
}
|
|
247
178
|
|
|
248
179
|
const models = resp.model_remains ?? [];
|
|
@@ -256,8 +187,10 @@ export async function fetchUsage(
|
|
|
256
187
|
if (weeklyWindow) windows.push(weeklyWindow);
|
|
257
188
|
}
|
|
258
189
|
|
|
190
|
+
if (windows.length === 0)
|
|
191
|
+
return unavailable({ kind: "no_quota", detail: "No active coding plan" });
|
|
259
192
|
return {
|
|
260
|
-
status:
|
|
193
|
+
status: "available",
|
|
261
194
|
planLabel: undefined,
|
|
262
195
|
windows,
|
|
263
196
|
balances: [],
|
|
@@ -265,24 +198,55 @@ export async function fetchUsage(
|
|
|
265
198
|
};
|
|
266
199
|
}
|
|
267
200
|
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
const
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
} catch {
|
|
277
|
-
/* Check config next. */
|
|
201
|
+
async function readAuth(
|
|
202
|
+
input: UsageInput,
|
|
203
|
+
): Promise<(MiniMaxResolvedAuth & { expiresAt?: string }) | undefined> {
|
|
204
|
+
if (input.store === "env") {
|
|
205
|
+
const token = process.env[input.locator];
|
|
206
|
+
return token
|
|
207
|
+
? { token, baseUrl: resolveBaseUrl({ baseUrl: process.env.MINIMAX_BASE_URL }) }
|
|
208
|
+
: undefined;
|
|
278
209
|
}
|
|
279
210
|
try {
|
|
280
|
-
const
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
211
|
+
const raw: unknown = JSON.parse(await fs.readFile(input.locator, "utf8"));
|
|
212
|
+
if (input.store === "credentials") {
|
|
213
|
+
const credentials = MiniMaxCredentialsSchema.parse(raw);
|
|
214
|
+
return credentials.access_token
|
|
215
|
+
? {
|
|
216
|
+
token: credentials.access_token,
|
|
217
|
+
baseUrl: resolveBaseUrl({ baseUrl: credentials.resource_url }),
|
|
218
|
+
expiresAt: credentials.expires_at ?? undefined,
|
|
219
|
+
}
|
|
220
|
+
: undefined;
|
|
221
|
+
}
|
|
222
|
+
const config = MiniMaxConfigSchema.parse(raw);
|
|
223
|
+
if (config.api_key)
|
|
224
|
+
return {
|
|
225
|
+
token: config.api_key,
|
|
226
|
+
baseUrl: resolveBaseUrl({ baseUrl: config.base_url, region: config.region }),
|
|
227
|
+
};
|
|
228
|
+
return config.oauth?.access_token
|
|
229
|
+
? {
|
|
230
|
+
token: config.oauth.access_token,
|
|
231
|
+
baseUrl: resolveBaseUrl({
|
|
232
|
+
baseUrl: config.oauth.resource_url ?? config.base_url,
|
|
233
|
+
region: config.region,
|
|
234
|
+
}),
|
|
235
|
+
expiresAt: config.oauth.expires_at ?? undefined,
|
|
236
|
+
}
|
|
237
|
+
: undefined;
|
|
284
238
|
} catch {
|
|
285
|
-
|
|
239
|
+
return undefined;
|
|
286
240
|
}
|
|
287
|
-
|
|
241
|
+
}
|
|
242
|
+
export async function discover(): Promise<UsageAccount[]> {
|
|
243
|
+
const candidates: UsageInput[] = [
|
|
244
|
+
{ store: "env", locator: "MINIMAX_API_KEY" },
|
|
245
|
+
{ store: "credentials", locator: join(homedir(), ".mmx", "credentials.json") },
|
|
246
|
+
{ store: "config", locator: join(homedir(), ".mmx", "config.json") },
|
|
247
|
+
];
|
|
248
|
+
const accounts: UsageAccount[] = [];
|
|
249
|
+
for (const input of candidates)
|
|
250
|
+
if (await readAuth(input)) accounts.push({ key: "default", input });
|
|
251
|
+
return accounts;
|
|
288
252
|
}
|
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
export const inputSchema = z
|
|
3
|
-
|
|
2
|
+
export const inputSchema = z
|
|
3
|
+
.object({
|
|
4
|
+
store: z.enum(["env", "credentials", "config"]),
|
|
5
|
+
locator: z.string().min(1),
|
|
6
|
+
})
|
|
7
|
+
.strict();
|
|
4
8
|
export type UsageInput = z.infer<typeof inputSchema>;
|
|
@@ -5,7 +5,7 @@ import type { UsageSourceRegistration } from "@getpaseo/plugin/server";
|
|
|
5
5
|
import type { ProviderLaunch } from "@getpaseo/plugin/server/provider";
|
|
6
6
|
import {
|
|
7
7
|
hashAccountKey,
|
|
8
|
-
|
|
8
|
+
unavailable,
|
|
9
9
|
windowFromUsedPct,
|
|
10
10
|
toneFromUsedPct,
|
|
11
11
|
} from "@getpaseo/plugin/server/usage";
|
|
@@ -59,18 +59,19 @@ export class Usage {
|
|
|
59
59
|
label: "Muse Code",
|
|
60
60
|
icon: "icon.svg",
|
|
61
61
|
input: inputSchema,
|
|
62
|
-
discover: async () =>
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
62
|
+
discover: async () =>
|
|
63
|
+
[...this.launches.keys()].map((account) => ({
|
|
64
|
+
key: account,
|
|
65
|
+
label: "Muse Code",
|
|
66
|
+
input: { account },
|
|
67
|
+
})),
|
|
67
68
|
fetch: async (input) => {
|
|
68
69
|
const { account } = inputSchema.parse(input);
|
|
69
70
|
const launch = this.launches.get(account);
|
|
70
|
-
if (!launch)
|
|
71
|
+
if (!launch) throw new Error("Muse login no longer exists");
|
|
71
72
|
try {
|
|
72
73
|
const { usage } = await this.read(account, launch);
|
|
73
|
-
if (!usage) return
|
|
74
|
+
if (!usage) return unavailable({ kind: "no_quota", detail: "No usage quota reported" });
|
|
74
75
|
return {
|
|
75
76
|
status: "available",
|
|
76
77
|
planLabel: usage.tier,
|
|
@@ -105,7 +106,6 @@ export class Usage {
|
|
|
105
106
|
} catch (error) {
|
|
106
107
|
return {
|
|
107
108
|
status: "error",
|
|
108
|
-
windows: [],
|
|
109
109
|
error: error instanceof Error ? error.message : String(error),
|
|
110
110
|
};
|
|
111
111
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { PluginServerContext } from "@getpaseo/plugin/server";
|
|
2
2
|
import { inputSchema } from "./shared/input.js";
|
|
3
|
-
import { discover, fetchUsage
|
|
3
|
+
import { discover, fetchUsage } from "./server/usage.js";
|
|
4
4
|
|
|
5
5
|
export default function contribute(server: PluginServerContext) {
|
|
6
6
|
server.registerUsageSource({
|
|
@@ -9,7 +9,6 @@ export default function contribute(server: PluginServerContext) {
|
|
|
9
9
|
icon: "icon.svg",
|
|
10
10
|
input: inputSchema,
|
|
11
11
|
discover,
|
|
12
|
-
identify,
|
|
13
12
|
fetch: fetchUsage,
|
|
14
13
|
});
|
|
15
14
|
return () => {};
|
|
@@ -4,6 +4,8 @@ import { join } from "node:path";
|
|
|
4
4
|
import { z } from "zod";
|
|
5
5
|
import {
|
|
6
6
|
hashAccountKey,
|
|
7
|
+
unavailable,
|
|
8
|
+
type UsageAccount,
|
|
7
9
|
toneFromUsedPct,
|
|
8
10
|
windowFromUsedPct,
|
|
9
11
|
type UsageReport,
|
|
@@ -44,23 +46,22 @@ export async function readDefaultKey(path = authPath()): Promise<string | null>
|
|
|
44
46
|
}
|
|
45
47
|
}
|
|
46
48
|
|
|
47
|
-
export async function discover(path = authPath()): Promise<
|
|
48
|
-
return (await readDefaultKey(path)) ? [{}] : [];
|
|
49
|
+
export async function discover(path = authPath()): Promise<UsageAccount[]> {
|
|
50
|
+
return (await readDefaultKey(path)) ? [{ key: hashAccountKey(path), input: { path } }] : [];
|
|
49
51
|
}
|
|
50
52
|
|
|
51
53
|
export async function fetchUsage(
|
|
52
|
-
|
|
54
|
+
input: Input,
|
|
53
55
|
fetchApi: typeof fetch = fetch,
|
|
54
|
-
path = authPath(),
|
|
55
56
|
): Promise<UsageReport> {
|
|
56
|
-
const apiKey = await readDefaultKey(path);
|
|
57
|
-
if (!apiKey)
|
|
57
|
+
const apiKey = await readDefaultKey(input.path);
|
|
58
|
+
if (!apiKey) throw new Error("OpenCode Go login store no longer exists");
|
|
58
59
|
const response = await fetchApi("https://opencode.ai/zen/go/v1/usage", {
|
|
59
60
|
headers: { Authorization: `Bearer ${apiKey}`, Accept: "application/json" },
|
|
60
61
|
signal: AbortSignal.timeout(15_000),
|
|
61
62
|
});
|
|
62
63
|
if (response.status === 401 || response.status === 403)
|
|
63
|
-
return {
|
|
64
|
+
return unavailable({ kind: "rejected", status: response.status, refreshedBy: "opencode" });
|
|
64
65
|
if (!response.ok) throw new Error(`OpenCode Go usage API returned ${response.status}`);
|
|
65
66
|
const data = responseSchema.parse(await response.json());
|
|
66
67
|
const windows = (
|
|
@@ -82,8 +83,3 @@ export async function fetchUsage(
|
|
|
82
83
|
);
|
|
83
84
|
return { status: "available", planLabel: "Go", windows };
|
|
84
85
|
}
|
|
85
|
-
|
|
86
|
-
export async function identify(_input: Input, path = authPath()) {
|
|
87
|
-
const key = await readDefaultKey(path);
|
|
88
|
-
return key ? { key: hashAccountKey(key) } : null;
|
|
89
|
-
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { PluginServerContext } from "@getpaseo/plugin/server";
|
|
2
2
|
import { inputSchema } from "./shared/input.js";
|
|
3
|
-
import { fetchUsage,
|
|
3
|
+
import { fetchUsage, discover } from "./server/usage.js";
|
|
4
4
|
|
|
5
5
|
export default function contribute(server: PluginServerContext) {
|
|
6
6
|
server.registerUsageSource({
|
|
@@ -8,8 +8,7 @@ export default function contribute(server: PluginServerContext) {
|
|
|
8
8
|
label: "Z.ai",
|
|
9
9
|
icon: "icon.svg",
|
|
10
10
|
input: inputSchema,
|
|
11
|
-
discover:
|
|
12
|
-
identify,
|
|
11
|
+
discover: () => discover(),
|
|
13
12
|
fetch: fetchUsage,
|
|
14
13
|
});
|
|
15
14
|
return () => {};
|