@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.
Files changed (3) hide show
  1. package/README.md +23 -7
  2. package/index.ts +10 -25
  3. 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
- V1 supports OpenAI Codex models by reading Pi's auth state from `~/.pi/agent/auth.json` (or `$PI_CODING_AGENT_DIR/auth.json`) and displays a subscription footer status after Pi's built-in status/token usage line only while the active Pi model provider is `openai-codex`.
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 include `access` and `accountId` fields.
78
- - `pi-sub` redacts auth/token-related errors and never prints Codex tokens.
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. V1 intentionally supports only OpenAI Codex and vendors only the small amount of behavior needed for this extension instead of invoking `codex-auth` directly.
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: string }> {
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 || typeof entry.key !== "string") throw new Error("Missing opencode-go API key in Pi auth");
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(signal?: AbortSignal): Promise<SubscriptionUsageSnapshot> {
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) return `${snapshot.providerDisplayName}: no accounts found.`;
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" },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bacnh85/pi-sub",
3
- "version": "0.1.6",
3
+ "version": "0.1.7",
4
4
  "description": "Pi extension showing subscription usage for supported model providers.",
5
5
  "type": "module",
6
6
  "license": "MIT",