@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.
Files changed (48) hide show
  1. package/dist/server/builtin-plugins/claude-usage-source/index.server.ts +1 -2
  2. package/dist/server/builtin-plugins/claude-usage-source/server/stores.ts +0 -1
  3. package/dist/server/builtin-plugins/claude-usage-source/server/usage.ts +72 -36
  4. package/dist/server/builtin-plugins/codex-usage-source/index.server.ts +1 -2
  5. package/dist/server/builtin-plugins/codex-usage-source/server/stores.ts +0 -1
  6. package/dist/server/builtin-plugins/codex-usage-source/server/usage.ts +22 -12
  7. package/dist/server/builtin-plugins/copilot-usage-source/index.server.ts +2 -3
  8. package/dist/server/builtin-plugins/copilot-usage-source/server/usage.ts +28 -39
  9. package/dist/server/builtin-plugins/copilot-usage-source/shared/input.ts +6 -2
  10. package/dist/server/builtin-plugins/cursor-usage-source/index.server.ts +2 -3
  11. package/dist/server/builtin-plugins/cursor-usage-source/server/usage.ts +53 -54
  12. package/dist/server/builtin-plugins/cursor-usage-source/shared/input.ts +6 -2
  13. package/dist/server/builtin-plugins/grok-usage-source/index.server.ts +2 -3
  14. package/dist/server/builtin-plugins/grok-usage-source/server/usage.ts +23 -27
  15. package/dist/server/builtin-plugins/grok-usage-source/shared/input.ts +6 -2
  16. package/dist/server/builtin-plugins/kimi-usage-source/index.server.ts +2 -3
  17. package/dist/server/builtin-plugins/kimi-usage-source/server/usage.ts +44 -67
  18. package/dist/server/builtin-plugins/kimi-usage-source/shared/input.ts +6 -2
  19. package/dist/server/builtin-plugins/minimax-usage-source/index.server.ts +2 -3
  20. package/dist/server/builtin-plugins/minimax-usage-source/server/usage.ts +64 -100
  21. package/dist/server/builtin-plugins/minimax-usage-source/shared/input.ts +6 -2
  22. package/dist/server/builtin-plugins/muse-provider/server/usage.ts +9 -9
  23. package/dist/server/builtin-plugins/opencode-go-usage-source/index.server.ts +1 -2
  24. package/dist/server/builtin-plugins/opencode-go-usage-source/server/usage.ts +8 -12
  25. package/dist/server/builtin-plugins/opencode-go-usage-source/shared/input.ts +1 -2
  26. package/dist/server/builtin-plugins/package.json +1 -1
  27. package/dist/server/builtin-plugins/zai-usage-source/index.server.ts +2 -3
  28. package/dist/server/builtin-plugins/zai-usage-source/server/usage.ts +13 -11
  29. package/dist/server/builtin-plugins/zai-usage-source/shared/input.ts +6 -2
  30. package/dist/server/server/plugins/index.d.ts +18 -3
  31. package/dist/server/server/plugins/index.js +1 -2
  32. package/dist/server/server/plugins/plugin-process-protocol.d.ts +0 -5
  33. package/dist/server/server/plugins/plugin-process-protocol.js +0 -8
  34. package/dist/server/server/plugins/plugin-process.js +2 -7
  35. package/dist/server/server/plugins/runtime.d.ts +0 -1
  36. package/dist/server/server/plugins/runtime.js +0 -11
  37. package/dist/server/server/plugins/test-fixtures/usage-source/index.server.js +11 -2
  38. package/dist/server/server/plugins/test-fixtures/usage-source-directory/index.server.js +11 -2
  39. package/dist/server/server/plugins/usage-sources/index.d.ts +6 -8
  40. package/dist/server/server/plugins/usage-sources/index.js +79 -84
  41. package/dist/server/web-ui/_expo/static/js/web/{index-a43718759ebeb27df202e8b9fc2fe58a.js → index-1a3199cb049d45e47bfd4c216ce98838.js} +8 -8
  42. package/dist/server/web-ui/_expo/static/js/web/index-1a3199cb049d45e47bfd4c216ce98838.js.br +0 -0
  43. package/dist/server/web-ui/_expo/static/js/web/{index-a43718759ebeb27df202e8b9fc2fe58a.js.gz → index-1a3199cb049d45e47bfd4c216ce98838.js.gz} +0 -0
  44. package/dist/server/web-ui/index.html +1 -1
  45. package/dist/server/web-ui/index.html.br +0 -0
  46. package/dist/server/web-ui/index.html.gz +0 -0
  47. package/package.json +6 -6
  48. 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 { existsSync, promises as fs } from "node:fs";
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
- unavailableUsage,
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
- void input;
113
- const homeDir = homedir();
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 (!res.ok) {
142
- return unavailableUsage();
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
- export async function identify() {
159
- if (process.env["GROK_API_KEY"] || process.env["GROK_TOKEN"]) return { key: "default" };
144
+ async function readToken(input: UsageInput): Promise<string | undefined> {
145
+ if (input.store === "env") return process.env[input.locator];
160
146
  try {
161
- const auth = JSON.parse(await fs.readFile(join(homedir(), ".grok", "auth.json"), "utf8"));
162
- return extractGrokTokenFromAuth(auth) ? { key: "default" } : null;
147
+ return (
148
+ extractGrokTokenFromAuth(JSON.parse(await fs.readFile(input.locator, "utf8"))) ?? undefined
149
+ );
163
150
  } catch {
164
- return null;
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,4 +1,8 @@
1
1
  import { z } from "zod";
2
- export const inputSchema = z.object({}).strict();
3
-
2
+ export const inputSchema = z
3
+ .object({
4
+ store: z.enum(["env", "file"]),
5
+ locator: z.string().min(1),
6
+ })
7
+ .strict();
4
8
  export type UsageInput = z.infer<typeof inputSchema>;
@@ -1,6 +1,6 @@
1
1
  import type { PluginServerContext } from "@getpaseo/plugin/server";
2
2
  import { inputSchema } from "./shared/input.js";
3
- import { fetchUsage, identify } from "./server/usage.js";
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: async () => [{}],
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 { existsSync, promises as fs } from "node:fs";
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
- unavailableUsage,
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
- async function readCredentials(): Promise<KimiCredentials | null> {
268
- const environmentToken = process.env["KIMI_TOKEN"] || process.env["KIMI_API_KEY"];
269
- if (environmentToken) {
270
- return { access_token: environmentToken };
271
- }
272
-
273
- for (const path of credentialPaths()) {
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 (!res.ok) {
309
- // Read-only on credentials; the Kimi CLI owns refresh. See docs/providers.md.
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
- export async function identify() {
326
- if (process.env["KIMI_TOKEN"] || process.env["KIMI_API_KEY"]) return { key: "default" };
327
- const home = homedir();
328
- const paths = [
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
- return null;
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,4 +1,8 @@
1
1
  import { z } from "zod";
2
- export const inputSchema = z.object({}).strict();
3
-
2
+ export const inputSchema = z
3
+ .object({
4
+ store: z.enum(["env", "file"]),
5
+ locator: z.string().min(1),
6
+ })
7
+ .strict();
4
8
  export type UsageInput = z.infer<typeof inputSchema>;
@@ -1,6 +1,6 @@
1
1
  import type { PluginServerContext } from "@getpaseo/plugin/server";
2
2
  import { inputSchema } from "./shared/input.js";
3
- import { fetchUsage, identify } from "./server/usage.js";
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: async () => [{}],
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 { existsSync, promises as fs } from "node:fs";
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
- unavailableUsage,
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
- void input;
152
- const configPath = join(homedir(), ".mmx", "config.json");
153
- const credentialsPath = join(homedir(), ".mmx", "credentials.json");
154
- const env = process.env;
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 (!res.ok) {
238
- return unavailableUsage();
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 unavailableUsage();
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: windows.length > 0 ? "available" : "unavailable",
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
- export async function identify() {
269
- if (process.env["MINIMAX_API_KEY"]) return { key: "default" };
270
- const home = join(homedir(), ".mmx");
271
- try {
272
- const credentials = MiniMaxCredentialsSchema.parse(
273
- JSON.parse(await fs.readFile(join(home, "credentials.json"), "utf8")),
274
- );
275
- if (credentials.access_token) return { key: "default" };
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 config = MiniMaxConfigSchema.parse(
281
- JSON.parse(await fs.readFile(join(home, "config.json"), "utf8")),
282
- );
283
- if (config.api_key || config.oauth?.access_token) return { key: "default" };
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
- /* No configured account. */
239
+ return undefined;
286
240
  }
287
- return null;
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.object({}).strict();
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
- unavailableUsage,
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 () => [...this.launches.keys()].map((account) => ({ account })),
63
- identify: async (input) => {
64
- const { account } = inputSchema.parse(input);
65
- return this.launches.has(account) ? { key: account, label: "Muse Code" } : null;
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) return unavailableUsage();
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 unavailableUsage();
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, identify } from "./server/usage.js";
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<Array<{}>> {
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
- _input: Input,
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) return { status: "unavailable", windows: [] };
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 { status: "unavailable", windows: [] };
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,4 +1,3 @@
1
1
  import { z } from "zod";
2
-
3
- export const inputSchema = z.object({}).strict();
2
+ export const inputSchema = z.object({ path: z.string().min(1) }).strict();
4
3
  export type Input = z.infer<typeof inputSchema>;
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@getpaseo/builtin-plugins",
3
- "version": "0.11.0-beta.2",
3
+ "version": "0.11.0-beta.3",
4
4
  "private": true,
5
5
  "type": "module",
6
6
  "scripts": {
@@ -1,6 +1,6 @@
1
1
  import type { PluginServerContext } from "@getpaseo/plugin/server";
2
2
  import { inputSchema } from "./shared/input.js";
3
- import { fetchUsage, identify } from "./server/usage.js";
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: async () => [{}],
12
- identify,
11
+ discover: () => discover(),
13
12
  fetch: fetchUsage,
14
13
  });
15
14
  return () => {};