pi-credits 0.2.0 → 0.3.0

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/README.md CHANGED
@@ -12,6 +12,7 @@ A [pi](https://pi.dev/) extension that shows the active model provider's credit
12
12
 
13
13
  - DeepSeek
14
14
  - Fireworks
15
+ - Moonshot
15
16
  - OpenAI Codex
16
17
  - OpenRouter
17
18
  - Vercel AI Gateway
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-credits",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "A pi extension that shows the active provider's credit balance or rate-limit usage",
5
5
  "keywords": [
6
6
  "pi-coding-agent",
@@ -1,10 +1,9 @@
1
- import { toNumber } from "../utils";
1
+ import { convertToUSD, toNumber } from "../utils";
2
2
 
3
3
  import type { Credits, CreditsProvider } from "../types";
4
4
 
5
5
  const PROVIDER = "deepseek";
6
6
  const URL = "https://api.deepseek.com/user/balance";
7
- const FRANKFURTER_API = "https://api.frankfurter.dev/v2/rate";
8
7
 
9
8
  interface DeepSeekBalanceResponse {
10
9
  balance_infos?: DeepSeekBalanceInfo[] | null;
@@ -15,25 +14,6 @@ interface DeepSeekBalanceInfo {
15
14
  total_balance?: string | number;
16
15
  }
17
16
 
18
- interface FrankfurterRateResponse {
19
- rate?: string | number;
20
- }
21
-
22
- async function convertToUSD(amount: number | undefined, currency: string | undefined, signal: AbortSignal): Promise<number | undefined> {
23
- if (amount === undefined) return undefined;
24
- if (!currency || currency === "USD") return amount;
25
-
26
- const url = `${FRANKFURTER_API}/${encodeURIComponent(currency)}/USD`;
27
- const response = await fetch(url, { headers: { Accept: "application/json" }, signal });
28
- if (!response.ok) throw new Error("currency conversion failed");
29
-
30
- const payload = (await response.json()) as FrankfurterRateResponse;
31
- const rate = toNumber(payload.rate);
32
- if (rate === undefined) throw new Error("currency conversion failed");
33
-
34
- return amount * rate;
35
- }
36
-
37
17
  export const deepseekProvider: CreditsProvider = {
38
18
  provider: PROVIDER,
39
19
  label: "DeepSeek",
@@ -1,5 +1,6 @@
1
1
  import { deepseekProvider } from "./deepseek";
2
2
  import { fireworksProvider } from "./fireworks";
3
+ import { moonshotProvider, moonshotCnProvider } from "./moonshot";
3
4
  import { openaiCodexProvider } from "./openai-codex";
4
5
  import { openrouterProvider } from "./openrouter";
5
6
  import { vercelAiGatewayProvider } from "./vercel-ai-gateway";
@@ -9,6 +10,8 @@ import type { CreditsProvider } from "../types";
9
10
  const PROVIDERS: CreditsProvider[] = [
10
11
  deepseekProvider,
11
12
  fireworksProvider,
13
+ moonshotProvider,
14
+ moonshotCnProvider,
12
15
  openaiCodexProvider,
13
16
  openrouterProvider,
14
17
  vercelAiGatewayProvider,
@@ -0,0 +1,39 @@
1
+ import { convertToUSD, toNumber } from "../utils";
2
+
3
+ import type { Credits, CreditsProvider } from "../types";
4
+
5
+ interface MoonshotBalanceResponse {
6
+ data?: {
7
+ available_balance?: string | number;
8
+ } | null;
9
+ }
10
+
11
+ /**
12
+ * For Moonshot, the international and China-mainland accounts live on separate hosts and bill in
13
+ * different currencies (USD vs CNY), which the endpoint does not report, so each pi provider ID
14
+ * fixes both host and currency.
15
+ */
16
+ function createMoonshotProvider(provider: string, host: string, currency: string): CreditsProvider {
17
+ return {
18
+ provider,
19
+ label: "Moonshot",
20
+
21
+ async fetch(_ctx, apiKey, signal): Promise<Credits> {
22
+ const headers: Record<string, string> = {
23
+ Accept: "application/json",
24
+ Authorization: `Bearer ${apiKey}`,
25
+ };
26
+
27
+ const response = await fetch(`https://${host}/v1/users/me/balance`, { headers, signal });
28
+ if (!response.ok) throw new Error("request failed");
29
+
30
+ const payload = (await response.json()) as MoonshotBalanceResponse;
31
+ const remaining = await convertToUSD(toNumber(payload.data?.available_balance), currency, signal);
32
+
33
+ return { type: "balance", remaining };
34
+ },
35
+ };
36
+ }
37
+
38
+ export const moonshotProvider = createMoonshotProvider("moonshotai", "api.moonshot.ai", "USD");
39
+ export const moonshotCnProvider = createMoonshotProvider("moonshotai-cn", "api.moonshot.cn", "CNY");
package/src/utils.ts CHANGED
@@ -29,3 +29,24 @@ export function toNumber(value?: string | number | null): number | undefined {
29
29
  const parsed = typeof value === "number" ? value : Number(value);
30
30
  return Number.isFinite(parsed) ? parsed : undefined;
31
31
  }
32
+
33
+ const FRANKFURTER_API = "https://api.frankfurter.dev/v2/rate";
34
+
35
+ interface FrankfurterRateResponse {
36
+ rate?: string | number;
37
+ }
38
+
39
+ export async function convertToUSD(amount: number | undefined, currency: string | undefined, signal: AbortSignal): Promise<number | undefined> {
40
+ if (amount === undefined) return undefined;
41
+ if (!currency || currency === "USD") return amount;
42
+
43
+ const url = `${FRANKFURTER_API}/${encodeURIComponent(currency)}/USD`;
44
+ const response = await fetch(url, { headers: { Accept: "application/json" }, signal });
45
+ if (!response.ok) throw new Error("currency conversion failed");
46
+
47
+ const payload = (await response.json()) as FrankfurterRateResponse;
48
+ const rate = toNumber(payload.rate);
49
+ if (rate === undefined) throw new Error("currency conversion failed");
50
+
51
+ return amount * rate;
52
+ }