@bacnh85/pi-sub 0.1.6 → 0.1.7
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 +23 -7
- package/index.ts +10 -25
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Pi extension that shows subscription usage for the currently selected supported model provider.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Supports OpenAI Codex (`openai-codex`) with live usage windows from ChatGPT's usage endpoint, and OpenCode Go (`opencode-go`) with session cost tracking. Displays a subscription footer status after Pi's built-in status/token usage line.
|
|
6
6
|
|
|
7
7
|
## Install
|
|
8
8
|
|
|
@@ -26,6 +26,8 @@ pi -e ./extensions/pi-sub
|
|
|
26
26
|
|
|
27
27
|
## What it shows
|
|
28
28
|
|
|
29
|
+
### OpenAI Codex
|
|
30
|
+
|
|
29
31
|
The footer status appears after Pi's built-in status/token usage line and includes:
|
|
30
32
|
|
|
31
33
|
- active account email;
|
|
@@ -40,6 +42,14 @@ Example subscription line:
|
|
|
40
42
|
Sub · Plus · user@example.com · 5H 85% (18:12) · W 80% (08:00 on 2 Jul) · gpt-5-codex
|
|
41
43
|
```
|
|
42
44
|
|
|
45
|
+
### OpenCode Go
|
|
46
|
+
|
|
47
|
+
OpenCode Go does not expose usage windows, so the footer shows only the accumulated session cost:
|
|
48
|
+
|
|
49
|
+
```text
|
|
50
|
+
OpenCode Go $0.23
|
|
51
|
+
```
|
|
52
|
+
|
|
43
53
|
When the current model provider is not supported, `pi-sub` clears its subscription line and does not refresh subscription data.
|
|
44
54
|
|
|
45
55
|
## Commands
|
|
@@ -57,6 +67,14 @@ ACCOUNT PLAN 5H USAGE WEEKLY USAGE LAST ACTIVITY
|
|
|
57
67
|
* user@example.com Plus 85% (18:12) 80% (08:00 on 2 Jul) Now
|
|
58
68
|
```
|
|
59
69
|
|
|
70
|
+
For OpenCode Go, `/sub` shows the provider/model and session cost:
|
|
71
|
+
|
|
72
|
+
```text
|
|
73
|
+
Provider: OpenCode Go · Model: kimi-k2.6 · Fetched: 14:23
|
|
74
|
+
OpenCode Go does not expose usage windows.
|
|
75
|
+
Session cost: $0.23
|
|
76
|
+
```
|
|
77
|
+
|
|
60
78
|
## Refresh behavior
|
|
61
79
|
|
|
62
80
|
`pi-sub` refreshes usage data:
|
|
@@ -73,12 +91,10 @@ Refreshes are cached briefly to avoid excessive usage endpoint calls.
|
|
|
73
91
|
|
|
74
92
|
## Requirements and troubleshooting
|
|
75
93
|
|
|
76
|
-
- Pi auth must contain an `openai-codex` OAuth entry in `~/.pi/agent/auth.json` or `$PI_CODING_AGENT_DIR/auth.json`.
|
|
77
|
-
- The entry must
|
|
78
|
-
- `pi-sub` redacts auth/token-related errors and never prints
|
|
79
|
-
|
|
80
|
-
If usage is unavailable, verify that Pi can use the `openai-codex` provider and that the auth file exists.
|
|
94
|
+
- **OpenAI Codex**: Pi auth must contain an `openai-codex` OAuth entry in `~/.pi/agent/auth.json` or `$PI_CODING_AGENT_DIR/auth.json`. The entry must include `access` and `accountId` fields.
|
|
95
|
+
- **OpenCode Go**: Pi auth must contain an `opencode-go` API key entry (via `/login` or env var). The entry must have a `key` field or an `accountId` field.
|
|
96
|
+
- `pi-sub` redacts auth/token-related errors and never prints credentials.
|
|
81
97
|
|
|
82
98
|
## Design notes
|
|
83
99
|
|
|
84
|
-
The extension is named `pi-sub` rather than `pi-codex-usage` so future subscription providers can be added as separate adapters.
|
|
100
|
+
The extension is named `pi-sub` rather than `pi-codex-usage` so future subscription providers can be added as separate adapters. Supports OpenAI Codex (live usage API) and OpenCode Go (session cost only).
|
package/index.ts
CHANGED
|
@@ -230,25 +230,6 @@ function parseUsageResponse(body: unknown): UsageApiSnapshot | undefined {
|
|
|
230
230
|
};
|
|
231
231
|
}
|
|
232
232
|
|
|
233
|
-
function parseOpcodeUsageResponse(body: unknown): UsageApiSnapshot | undefined {
|
|
234
|
-
if (!body || typeof body !== "object") return undefined;
|
|
235
|
-
const root = body as any;
|
|
236
|
-
const usage = root.usage ?? root;
|
|
237
|
-
const parseWindow = (window: any): UsageApiWindow | undefined => {
|
|
238
|
-
if (!window || typeof window !== "object" || typeof window.used_percent !== "number") return undefined;
|
|
239
|
-
return {
|
|
240
|
-
used_percent: window.used_percent,
|
|
241
|
-
limit_window_seconds: typeof window.limit_window_seconds === "number" ? window.limit_window_seconds : undefined,
|
|
242
|
-
reset_at: typeof window.reset_at === "number" ? window.reset_at : typeof window.reset_ts === "number" ? window.reset_ts : undefined,
|
|
243
|
-
};
|
|
244
|
-
};
|
|
245
|
-
return {
|
|
246
|
-
primary: parseWindow(usage.rolling ?? usage.primary_window),
|
|
247
|
-
secondary: parseWindow(usage.weekly ?? usage.secondary_window),
|
|
248
|
-
plan_type: typeof root.plan_type === "string" ? root.plan_type : typeof usage.plan_type === "string" ? usage.plan_type : undefined,
|
|
249
|
-
};
|
|
250
|
-
}
|
|
251
|
-
|
|
252
233
|
async function readPiCodexAuth(): Promise<PiAuthEntry & { accountId: string }> {
|
|
253
234
|
const auth = await readJsonFile<PiAuthFile>(piAuthPath());
|
|
254
235
|
const entry = auth[CODEX_PROVIDER];
|
|
@@ -257,11 +238,12 @@ async function readPiCodexAuth(): Promise<PiAuthEntry & { accountId: string }> {
|
|
|
257
238
|
return { ...entry, accountId };
|
|
258
239
|
}
|
|
259
240
|
|
|
260
|
-
async function readOpenCodeGoAuth(): Promise<{ key
|
|
241
|
+
async function readOpenCodeGoAuth(): Promise<{ key?: string; accountId?: string }> {
|
|
261
242
|
const auth = await readJsonFile<PiAuthFile>(piAuthPath());
|
|
262
243
|
const entry = auth[OPC_PROVIDER];
|
|
263
|
-
if (!entry?.key
|
|
264
|
-
return { key: entry.key };
|
|
244
|
+
if (!entry?.key && !entry?.accountId) throw new Error("Missing opencode-go API key or accountId in Pi auth");
|
|
245
|
+
if (typeof entry.key === "string") return { key: entry.key };
|
|
246
|
+
return { accountId: typeof entry.accountId === "string" ? entry.accountId : undefined };
|
|
265
247
|
}
|
|
266
248
|
|
|
267
249
|
async function fetchUsageFromPiAuth(entry: PiAuthEntry, signal?: AbortSignal): Promise<UsageApiSnapshot | undefined> {
|
|
@@ -314,7 +296,7 @@ async function fetchCodexUsage(signal?: AbortSignal): Promise<SubscriptionUsageS
|
|
|
314
296
|
}
|
|
315
297
|
}
|
|
316
298
|
|
|
317
|
-
async function fetchOpenCodeGoUsage(
|
|
299
|
+
async function fetchOpenCodeGoUsage(_signal?: AbortSignal): Promise<SubscriptionUsageSnapshot> {
|
|
318
300
|
try {
|
|
319
301
|
await readOpenCodeGoAuth();
|
|
320
302
|
return {
|
|
@@ -322,7 +304,6 @@ async function fetchOpenCodeGoUsage(signal?: AbortSignal): Promise<SubscriptionU
|
|
|
322
304
|
providerDisplayName: "OpenCode Go",
|
|
323
305
|
accounts: [],
|
|
324
306
|
fetchedAt: Date.now(),
|
|
325
|
-
error: "OpenCode Go usage tracking not yet implemented",
|
|
326
307
|
};
|
|
327
308
|
} catch (error) {
|
|
328
309
|
return {
|
|
@@ -511,7 +492,11 @@ function buildDetails(snapshot: SubscriptionUsageSnapshot | undefined, state: St
|
|
|
511
492
|
if (!state.adapter) return `Subscription tracking inactive for current model provider (${state.model?.provider ?? "unknown"}).`;
|
|
512
493
|
if (!snapshot) return "Subscription usage has not been loaded yet.";
|
|
513
494
|
if (snapshot.error) return `${snapshot.providerDisplayName}: ${snapshot.error}`;
|
|
514
|
-
if (snapshot.accounts.length === 0)
|
|
495
|
+
if (snapshot.accounts.length === 0) {
|
|
496
|
+
const costLine = snapshot.cost !== undefined && snapshot.cost > 0 ? `\nSession cost: $${snapshot.cost.toFixed(2)}` : "";
|
|
497
|
+
const modelInfo = state.model?.id ? ` · Model: ${state.model.id}` : "";
|
|
498
|
+
return `Provider: ${snapshot.providerDisplayName}${modelInfo} · Fetched: ${new Date(snapshot.fetchedAt).toLocaleTimeString()}\n${snapshot.providerDisplayName} does not expose usage windows.${costLine}`;
|
|
499
|
+
}
|
|
515
500
|
|
|
516
501
|
const columns: { key: string; label: string; get: (a: SubscriptionAccountSnapshot) => string }[] = [
|
|
517
502
|
{ key: "account", label: "ACCOUNT", get: (a) => a.accountLabel ?? "unknown" },
|