@ogulcancelik/pi-minimal-footer 0.1.10 → 0.1.12
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 +2 -0
- package/index.ts +65 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -24,6 +24,7 @@ Minimal footer for [pi](https://github.com/earendil-works/pi) that replaces the
|
|
|
24
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
|
+
| OpenCode Go | 5h + weekly + monthly usage windows |
|
|
27
28
|
|
|
28
29
|
## Install
|
|
29
30
|
|
|
@@ -39,6 +40,7 @@ Environment variables (all optional):
|
|
|
39
40
|
| ------------------------------- | -------------------------------------------------------- | ------- |
|
|
40
41
|
| `PI_MINIMAL_FOOTER_SHOW_CWD` | Show current working directory in footer status line | `1` |
|
|
41
42
|
| `PI_MINIMAL_FOOTER_SHOW_BRANCH` | Show git branch/dirty/ahead/behind in footer status line | `1` |
|
|
43
|
+
| `PI_MINIMAL_FOOTER_SHOW_PROVIDER` | Show provider/full-model-id instead of the short model ID | `0` |
|
|
42
44
|
|
|
43
45
|
Accepted false values: `0`, `false`, `no`, `off` (case-insensitive).
|
|
44
46
|
|
package/index.ts
CHANGED
|
@@ -267,6 +267,10 @@ function getKimiToken(): string | undefined {
|
|
|
267
267
|
return getApiKey("kimi-coding", "KIMI_API_KEY");
|
|
268
268
|
}
|
|
269
269
|
|
|
270
|
+
function getOpencodeToken(): string | undefined {
|
|
271
|
+
return getApiKey("opencode-go", "OPENCODE_API_KEY");
|
|
272
|
+
}
|
|
273
|
+
|
|
270
274
|
// ============ Time Formatting ============
|
|
271
275
|
|
|
272
276
|
function formatResetTime(date: Date): string {
|
|
@@ -706,6 +710,58 @@ async function fetchKimiUsage(): Promise<UsageSnapshot> {
|
|
|
706
710
|
}
|
|
707
711
|
}
|
|
708
712
|
|
|
713
|
+
async function fetchOpencodeGoUsage(): Promise<UsageSnapshot> {
|
|
714
|
+
const providerLabel = "OpenCode Go";
|
|
715
|
+
const token = getOpencodeToken();
|
|
716
|
+
if (!token) {
|
|
717
|
+
return { provider: providerLabel, windows: [], error: "no-auth", fetchedAt: Date.now() };
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
try {
|
|
721
|
+
const res = await fetchWithTimeout("https://opencode.ai/zen/go/v1/usage", {
|
|
722
|
+
method: "GET",
|
|
723
|
+
headers: {
|
|
724
|
+
Authorization: `Bearer ${token}`,
|
|
725
|
+
},
|
|
726
|
+
});
|
|
727
|
+
|
|
728
|
+
if (!res.ok) {
|
|
729
|
+
return { provider: providerLabel, windows: [], error: `HTTP ${res.status}`, fetchedAt: Date.now() };
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
const data = (await res.json()) as any;
|
|
733
|
+
const windows: RateWindow[] = [];
|
|
734
|
+
|
|
735
|
+
if (data.rollingUsage) {
|
|
736
|
+
const usedPercent = clampPercent(data.rollingUsage.usagePercent ?? 0);
|
|
737
|
+
const resetsIn = Number.isFinite(data.rollingUsage.resetInSec)
|
|
738
|
+
? formatResetTime(new Date(Date.now() + data.rollingUsage.resetInSec * 1000))
|
|
739
|
+
: undefined;
|
|
740
|
+
windows.push({ label: "5h", usedPercent, resetsIn });
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
if (data.weeklyUsage) {
|
|
744
|
+
const usedPercent = clampPercent(data.weeklyUsage.usagePercent ?? 0);
|
|
745
|
+
const resetsIn = Number.isFinite(data.weeklyUsage.resetInSec)
|
|
746
|
+
? formatResetTime(new Date(Date.now() + data.weeklyUsage.resetInSec * 1000))
|
|
747
|
+
: undefined;
|
|
748
|
+
windows.push({ label: "Week", usedPercent, resetsIn });
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
if (data.monthlyUsage) {
|
|
752
|
+
const usedPercent = clampPercent(data.monthlyUsage.usagePercent ?? 0);
|
|
753
|
+
const resetsIn = Number.isFinite(data.monthlyUsage.resetInSec)
|
|
754
|
+
? formatResetTime(new Date(Date.now() + data.monthlyUsage.resetInSec * 1000))
|
|
755
|
+
: undefined;
|
|
756
|
+
windows.push({ label: "Month", usedPercent, resetsIn });
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
return { provider: providerLabel, windows, fetchedAt: Date.now() };
|
|
760
|
+
} catch (e) {
|
|
761
|
+
return { provider: providerLabel, windows: [], error: String(e), fetchedAt: Date.now() };
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
|
|
709
765
|
// ============ Provider Detection ============
|
|
710
766
|
|
|
711
767
|
// Map pi provider names to our internal usage provider keys
|
|
@@ -717,6 +773,7 @@ const PROVIDER_MAP: Record<string, string> = {
|
|
|
717
773
|
minimax: "minimax", // MiniMax Token Plan / Coding Plan
|
|
718
774
|
"minimax-cn": "minimax-cn", // MiniMax China plan
|
|
719
775
|
"kimi-coding": "kimi-coding", // Kimi plan
|
|
776
|
+
"opencode-go": "opencode-go", // OpenCode Go plan
|
|
720
777
|
};
|
|
721
778
|
|
|
722
779
|
function detectProvider(modelProvider: string): string | null {
|
|
@@ -739,6 +796,8 @@ async function fetchUsageForProvider(provider: string): Promise<UsageSnapshot> {
|
|
|
739
796
|
return fetchMinimaxUsage("minimax-cn");
|
|
740
797
|
case "kimi-coding":
|
|
741
798
|
return fetchKimiUsage();
|
|
799
|
+
case "opencode-go":
|
|
800
|
+
return fetchOpencodeGoUsage();
|
|
742
801
|
default:
|
|
743
802
|
return { provider: "Unknown", windows: [], error: "unknown-provider", fetchedAt: Date.now() };
|
|
744
803
|
}
|
|
@@ -756,6 +815,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
756
815
|
// Optional visibility toggles (default: enabled)
|
|
757
816
|
const showCwd = parseBooleanEnv(process.env.PI_MINIMAL_FOOTER_SHOW_CWD, true);
|
|
758
817
|
const showBranch = parseBooleanEnv(process.env.PI_MINIMAL_FOOTER_SHOW_BRANCH, true);
|
|
818
|
+
const showProvider = parseBooleanEnv(process.env.PI_MINIMAL_FOOTER_SHOW_PROVIDER, false);
|
|
759
819
|
|
|
760
820
|
function formatTokenCount(tokens: number): string {
|
|
761
821
|
if (tokens >= 1_000_000) {
|
|
@@ -1028,7 +1088,11 @@ export default function (pi: ExtensionAPI) {
|
|
|
1028
1088
|
}
|
|
1029
1089
|
|
|
1030
1090
|
// Model + thinking
|
|
1031
|
-
const modelName = ctx.model
|
|
1091
|
+
const modelName = ctx.model
|
|
1092
|
+
? showProvider
|
|
1093
|
+
? `${ctx.model.provider}/${ctx.model.id}`
|
|
1094
|
+
: ctx.model.id.split("/").pop() || "no-model"
|
|
1095
|
+
: "no-model";
|
|
1032
1096
|
const plainModelStr = theme.fg("muted", modelName);
|
|
1033
1097
|
let modelStr = plainModelStr;
|
|
1034
1098
|
if (ctx.model?.reasoning) {
|