@cad0p/pi-usage 0.11.2-20261102.0 → 0.11.3-20261102.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/CHANGELOG.md ADDED
@@ -0,0 +1,20 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ ## [0.11.3] - 2026-11-02
6
+
7
+ <!-- USER-EDITABLE SECTION START -->
8
+ First fix release of the fork. With Claude Code uninstalled, Claude now reads Pi's own login instead of a dead Claude Code store, and `/usage` shows every OpenCode Go account.
9
+
10
+ - **Claude source:** `auto` passes over an expired Claude Code token when Pi's `anthropic` login is fresh, and adds a warning naming the switch. `PI_QUOTA_CLAUDE_SOURCE=claude-code` still pins the CLI store. The report cache is keyed by the Claude source setting, so a switch refetches.
11
+ - **Multi-account panel:** a provider with several accounts is one header plus one line per account (its fullest window, or `key rejected` / `no Go plan`), rendered as a component so Pi's ten-line widget cap no longer truncates it.
12
+ - **Summary:** `OP-Go: 7/14 ok · lowest 24% (#7)` instead of account #1's number.
13
+ - `piquota --version` reads `package.json`.
14
+ <!-- USER-EDITABLE SECTION END -->
15
+
16
+ ### 🐛 Bug Fixes
17
+
18
+ - /usage live bugs — dead Claude store, truncated panel, account-#1 summary (closes #5)
19
+
20
+
package/README.md CHANGED
@@ -230,10 +230,10 @@ Inside Pi:
230
230
 
231
231
  | Command | Effect |
232
232
  | --- | --- |
233
- | `/usage` | one on-demand refresh, show the full panel, notify a one-line summary |
233
+ | `/usage` | one on-demand refresh, show the full panel until the next prompt (or a second `/usage`), notify a one-line summary |
234
234
  | `/usage box` | pin the five-row box above the editor |
235
235
  | `/usage line` | pin the compact line above the editor, in its own row |
236
- | `/usage panel` | full boxed panel above the editor |
236
+ | `/usage panel` | full panel above the editor, pinned across prompts |
237
237
  | `/usage hide` | hide the panel/line and stop background refresh until a surface is on again |
238
238
  | `/usage status` / `nostatus` | enable/disable the footer status |
239
239
  | `/usage refresh` | refresh only |
@@ -248,8 +248,9 @@ it fresh with a background refresh (and a gentle refresh after each agent turn).
248
248
  `/usage hide` gives the row back.
249
249
 
250
250
  A provider with several accounts is shown as one header (`OP-Go · 7 of 14 accounts
251
- ok`) plus one line per account: its fullest window, or the reason it failed (`key
252
- rejected`, `no Go plan`). The panel is rendered as a component, so Pi's ten-line
251
+ ok`) plus one line per account with every window and its reset, e.g.
252
+ `#2 ● session 2% (10m) · week 10% (6d) · month 90% (19d)`, or the reason it failed
253
+ (`key rejected`, `no Go plan`). The leading glyph is the fullest window's band. The panel is rendered as a component, so Pi's ten-line
253
254
  cap on plain widgets never truncates it, and the summary names the account with the
254
255
  most room: `OP-Go: 7/14 ok · lowest 24% (#7)`.
255
256
 
@@ -498,7 +499,7 @@ extensions/quota-panel.ts Pi TUI line and /usage
498
499
  ## Tests
499
500
 
500
501
  ```bash
501
- node --test tests/*.test.mjs # 265 tests, fake tokens only, no network
502
+ node --test tests/*.test.mjs # 266 tests, fake tokens only, no network
502
503
  ```
503
504
 
504
505
  Modules covered: `auth.json` parsing and de-duplication, the Claude Code store
@@ -1,7 +1,8 @@
1
1
  /**
2
2
  * Pi quota line — read-only quota for the providers Pi already owns.
3
3
  *
4
- * Surface: nothing at session start. /usage box, /usage line or /usage panel pins
4
+ * Surface: nothing at session start. Bare /usage shows the panel until the next
5
+ * prompt or a second /usage. /usage box, /usage line or /usage panel pins
5
6
  * a surface above the editor, /usage status enables the footer, and /usage hide
6
7
  * removes it. No timer or post-turn refresh runs until a surface is on.
7
8
  *
@@ -550,6 +551,18 @@ function summarize(report: QuotaReport): string {
550
551
  .join(" · ");
551
552
  }
552
553
 
554
+ /** Window names for the one-line account rows (same words as the Omarchy panel). */
555
+ const WINDOW_TAG: Record<string, string> = { "5h": "session", weekly: "week", monthly: "month" };
556
+
557
+ /** A reset countdown in its largest unit, so a row of three stays narrow. */
558
+ function shortDuration(seconds: number | null): string {
559
+ if (seconds === null || !Number.isFinite(seconds)) return "";
560
+ if (seconds <= 0) return "now";
561
+ if (seconds >= 86400) return `${Math.floor(seconds / 86400)}d`;
562
+ if (seconds >= 3600) return `${Math.floor(seconds / 3600)}h`;
563
+ return `${Math.max(1, Math.floor(seconds / 60))}m`;
564
+ }
565
+
553
566
  /** The account's fullest window: the one that stops its next prompt. */
554
567
  function bindingWindow(provider: QuotaProvider): QuotaWindow | null {
555
568
  let best: QuotaWindow | null = null;
@@ -600,13 +613,17 @@ function renderAccountRows(accounts: QuotaProvider[], theme: Theme): string[] {
600
613
  lines.push(` ${label} ${theme.fg("dim", "no rate-limit windows reported")}`);
601
614
  continue;
602
615
  }
616
+ // Every window on one line, each in its own band colour; the leading glyph
617
+ // is the fullest window's, so the column still sorts the accounts by eye.
603
618
  const band = bandFor(used);
604
- const reset = window.resetsInSec === null ? "" : theme.fg("dim", ` · reset in ${humanDuration(window.resetsInSec)}`);
605
- lines.push(
606
- ` ${label} ${paint(band.color, band.glyph)} ${paint(band.color, bar(used))} ` +
607
- `${paint(band.color, `${used.toFixed(used < 10 ? 1 : 0)}%`.padStart(5))} ` +
608
- `${theme.fg("dim", window.id)}${reset}`,
609
- );
619
+ const cells = account.windows
620
+ .filter((each) => usedOf(each) !== null)
621
+ .map((each) => {
622
+ const pct = usedOf(each) as number;
623
+ const reset = each.resetsInSec === null ? "" : ` ${theme.fg("dim", `(${shortDuration(each.resetsInSec)})`.padEnd(5))}`;
624
+ return `${theme.fg("dim", WINDOW_TAG[each.id] ?? each.id)} ${paint(bandFor(pct).color, `${Math.round(pct)}%`.padStart(4))}${reset}`;
625
+ });
626
+ lines.push(` ${label} ${paint(band.color, band.glyph)} ${cells.join(theme.fg("dim", " · "))}`);
610
627
  }
611
628
  return lines;
612
629
  }
@@ -686,6 +703,11 @@ export default function quotaPanelExtension(pi: ExtensionAPI): void {
686
703
  let timer: ReturnType<typeof setInterval> | null = null;
687
704
  let agentEndTimer: ReturnType<typeof setTimeout> | null = null;
688
705
  let panelVisible = false;
706
+ /**
707
+ * The panel bare `/usage` opens is a glance, not a pin: it goes away when the
708
+ * next prompt starts or when `/usage` is run again. `/usage panel` pins it.
709
+ */
710
+ let transientPanel = false;
689
711
  /**
690
712
  * Nothing is pinned by default. A box or line above the editor is a persistent
691
713
  * surface, and a quota command should not claim a row until asked.
@@ -933,7 +955,17 @@ export default function quotaPanelExtension(pi: ExtensionAPI): void {
933
955
  paintUi(ui);
934
956
  });
935
957
 
936
- pi.on("agent_start", () => {
958
+ function dismissTransientPanel(ui: UiContext): void {
959
+ panelVisible = false;
960
+ transientPanel = false;
961
+ if (!surfaceActive()) stopPolling();
962
+ paintUi(ui);
963
+ }
964
+
965
+ pi.on("agent_start", (_event, ctx) => {
966
+ // SAFETY: Pi's agent_start context implements UiContext when hasUI is true.
967
+ const ui = ctx as unknown as UiContext;
968
+ if (transientPanel && panelVisible && ui?.hasUI && !disposed) dismissTransientPanel(ui);
937
969
  if (agentEndTimer) {
938
970
  clearTimeout(agentEndTimer);
939
971
  agentEndTimer = null;
@@ -1002,6 +1034,7 @@ export default function quotaPanelExtension(pi: ExtensionAPI): void {
1002
1034
  return;
1003
1035
  case "panel":
1004
1036
  panelVisible = true;
1037
+ transientPanel = false;
1005
1038
  lineVisible = false;
1006
1039
  boxVisible = false;
1007
1040
  explicitBox = false;
@@ -1048,8 +1081,15 @@ export default function quotaPanelExtension(pi: ExtensionAPI): void {
1048
1081
  break;
1049
1082
  }
1050
1083
 
1084
+ // Bare /usage again while its glance is open closes it.
1085
+ if (panelVisible && transientPanel) {
1086
+ dismissTransientPanel(ctx);
1087
+ return;
1088
+ }
1089
+
1051
1090
  // Bare /usage: one on-demand refresh, show the panel, report the percentages.
1052
1091
  panelVisible = true;
1092
+ transientPanel = true;
1053
1093
  lineVisible = false;
1054
1094
  boxVisible = false;
1055
1095
  await refresh(ctx, true);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cad0p/pi-usage",
3
- "version": "0.11.2-20261102.0",
3
+ "version": "0.11.3-20261102.0",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },