@ogulcancelik/pi-minimal-footer 0.1.8 → 0.1.10

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 (3) hide show
  1. package/README.md +1 -1
  2. package/index.ts +31 -19
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -21,7 +21,7 @@ Minimal footer for [pi](https://github.com/earendil-works/pi) that replaces the
21
21
  | OpenAI Codex | Primary + secondary rolling windows |
22
22
  | GitHub Copilot | Premium interactions + chat quotas |
23
23
  | Google Gemini | Pro + Flash remaining quotas |
24
- | MiniMax | 5h + weekly rolling windows (Token Plan / Coding Plan) |
24
+ | MiniMax | 5h + weekly rolling windows (Token Plan, credit-based) |
25
25
  | MiniMax CN | Same as MiniMax, China endpoint |
26
26
  | Kimi Coding | 5h + weekly rolling windows (Plan) |
27
27
 
package/index.ts CHANGED
@@ -4,7 +4,7 @@
4
4
  * Custom footer with context gauge + subscription usage bars.
5
5
  * Auto-detects provider from current model and shows relevant usage.
6
6
  *
7
- * Supports: Claude Max, Codex, Copilot, Gemini
7
+ * Supports: Claude Max, Codex, Copilot, Gemini, MiniMax Token Plan, Kimi Coding
8
8
  */
9
9
 
10
10
  import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
@@ -544,10 +544,12 @@ async function fetchGeminiUsage(): Promise<UsageSnapshot> {
544
544
  async function fetchMinimaxUsage(provider: "minimax" | "minimax-cn"): Promise<UsageSnapshot> {
545
545
  const token = getMinimaxToken(provider);
546
546
  const providerLabel = provider === "minimax-cn" ? "MiniMax CN" : "MiniMax";
547
+ // Docs-recommended Token Plan endpoint. The legacy /coding_plan/remains path
548
+ // still works but the response field names differ from the current UI.
547
549
  const endpoint =
548
550
  provider === "minimax-cn"
549
- ? "https://api.minimaxi.com/v1/api/openplatform/coding_plan/remains"
550
- : "https://api.minimax.io/v1/api/openplatform/coding_plan/remains";
551
+ ? "https://api.minimaxi.com/v1/token_plan/remains"
552
+ : "https://api.minimax.io/v1/token_plan/remains";
551
553
 
552
554
  if (!token) {
553
555
  return { provider: providerLabel, windows: [], error: "no-auth", fetchedAt: Date.now() };
@@ -578,9 +580,16 @@ async function fetchMinimaxUsage(provider: "minimax" | "minimax-cn"): Promise<Us
578
580
  }
579
581
 
580
582
  const remains = Array.isArray(data?.model_remains) ? data.model_remains : [];
583
+ // Token Plan returns one bucket per capability (general = text/code, video, etc.).
584
+ // Prefer the active "general" bucket since M-series chat models land there.
585
+ // status === 1 = window is active/limiting, 3 = inactive (no usage). Fall back
586
+ // to the first active bucket, then the first bucket of any kind.
581
587
  const textBucket =
582
- remains.find((entry: any) => typeof entry?.model_name === "string" && /^minimax-m/i.test(entry.model_name)) ||
583
- remains.find((entry: any) => typeof entry?.model_name === "string" && /minimax/i.test(entry.model_name)) ||
588
+ remains.find(
589
+ (entry: any) => entry?.model_name === "general" && Number(entry?.current_interval_status) === 1
590
+ ) ||
591
+ remains.find((entry: any) => entry?.model_name === "general") ||
592
+ remains.find((entry: any) => Number(entry?.current_interval_status) === 1) ||
584
593
  remains[0];
585
594
 
586
595
  if (!textBucket) {
@@ -589,12 +598,12 @@ async function fetchMinimaxUsage(provider: "minimax" | "minimax-cn"): Promise<Us
589
598
 
590
599
  const windows: RateWindow[] = [];
591
600
 
592
- const intervalTotal = Number(textBucket.current_interval_total_count) || 0;
593
- const intervalRemaining = Number(textBucket.current_interval_usage_count) || 0;
594
- if (intervalTotal > 0) {
595
- // current_*_usage_count = remaining (confirmed against MiniMax website UI)
596
- const used = intervalTotal - intervalRemaining;
597
- const usedPercent = clampPercent((used / intervalTotal) * 100);
601
+ // Source of truth: *_remaining_percent (the *_total_count / *_usage_count
602
+ // fields are zeroed in the credit-based model and cannot be used to compute
603
+ // a fraction). The UI usage bar maps 100 - remainingPercent -> used.
604
+ const intervalRemaining = Number(textBucket.current_interval_remaining_percent);
605
+ if (Number.isFinite(intervalRemaining)) {
606
+ const usedPercent = clampPercent(100 - intervalRemaining);
598
607
  const resetDate = textBucket.end_time ? new Date(Number(textBucket.end_time)) : undefined;
599
608
  const durationMs =
600
609
  textBucket.start_time && textBucket.end_time
@@ -607,13 +616,12 @@ async function fetchMinimaxUsage(provider: "minimax" | "minimax-cn"): Promise<Us
607
616
  });
608
617
  }
609
618
 
610
- const weeklyTotal = Number(textBucket.current_weekly_total_count) || 0;
611
- const weeklyRemaining = Number(textBucket.current_weekly_usage_count) || 0;
612
- if (weeklyTotal > 0) {
613
- // current_*_usage_count = remaining (confirmed against MiniMax website UI)
614
- const used = weeklyTotal - weeklyRemaining;
615
- const usedPercent = clampPercent((used / weeklyTotal) * 100);
616
- const resetDate = textBucket.weekly_end_time ? new Date(Number(textBucket.weekly_end_time)) : undefined;
619
+ const weeklyRemaining = Number(textBucket.current_weekly_remaining_percent);
620
+ if (Number.isFinite(weeklyRemaining)) {
621
+ const usedPercent = clampPercent(100 - weeklyRemaining);
622
+ const resetDate = textBucket.weekly_end_time
623
+ ? new Date(Number(textBucket.weekly_end_time))
624
+ : undefined;
617
625
  const durationMs =
618
626
  textBucket.weekly_start_time && textBucket.weekly_end_time
619
627
  ? Number(textBucket.weekly_end_time) - Number(textBucket.weekly_start_time)
@@ -625,6 +633,10 @@ async function fetchMinimaxUsage(provider: "minimax" | "minimax-cn"): Promise<Us
625
633
  });
626
634
  }
627
635
 
636
+ if (windows.length === 0) {
637
+ return { provider: providerLabel, windows: [], error: "no-usage-data", fetchedAt: Date.now() };
638
+ }
639
+
628
640
  return { provider: providerLabel, windows, fetchedAt: Date.now() };
629
641
  } catch (e) {
630
642
  return { provider: providerLabel, windows: [], error: String(e), fetchedAt: Date.now() };
@@ -1000,7 +1012,7 @@ export default function (pi: ExtensionAPI) {
1000
1012
  const { percentage, used: ctxUsed, total: ctxTotal } = getContextInfo(ctx);
1001
1013
 
1002
1014
  // Build parts for status line
1003
- let pwd = process.cwd();
1015
+ let pwd = ctx.cwd;
1004
1016
  const home = process.env.HOME || process.env.USERPROFILE;
1005
1017
  if (home && pwd.startsWith(home)) {
1006
1018
  pwd = `~${pwd.slice(home.length)}`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ogulcancelik/pi-minimal-footer",
3
- "version": "0.1.8",
3
+ "version": "0.1.10",
4
4
  "description": "Minimal footer for pi with context gauge and subscription usage bars",
5
5
  "type": "module",
6
6
  "keywords": [