@hk_net/pi-usage-bars 0.4.0 → 0.4.1
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 +12 -0
- package/README.md +1 -0
- package/docs/releasing.md +35 -57
- package/extensions/usage-bars/core.ts +38 -10
- package/extensions/usage-bars/index.ts +22 -13
- package/package.json +4 -5
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## Unreleased
|
|
4
|
+
|
|
5
|
+
## [0.4.1] - 2026-07-31
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
|
|
9
|
+
- Classify Codex quota windows by `limit_window_seconds` instead of assuming `primary_window` is always the session limit. Codex accounts that expose only a seven-day primary window now show it as Weekly and no longer display a fabricated `Session 0%` lane.
|
|
10
|
+
|
|
11
|
+
### Changed
|
|
12
|
+
|
|
13
|
+
- Updated the development and test baseline to Pi SDK 0.83.0 while retaining runtime compatibility with Pi 0.81.1 and newer.
|
|
14
|
+
|
|
3
15
|
## [0.4.0] - 2026-07-22
|
|
4
16
|
|
|
5
17
|
### Added
|
package/README.md
CHANGED
|
@@ -124,3 +124,4 @@ This extension is based on and inspired by:
|
|
|
124
124
|
|
|
125
125
|
- [CodexBar](https://github.com/steipete/CodexBar)
|
|
126
126
|
- [rho usage-bars](https://github.com/mikeyobrien/rho/tree/main/extensions/usage-bars)
|
|
127
|
+
- [ajarellanod/pi-usage-bars](https://github.com/ajarellanod/pi-usage-bars)
|
package/docs/releasing.md
CHANGED
|
@@ -2,105 +2,83 @@
|
|
|
2
2
|
|
|
3
3
|
This repository uses semantic versions. The package version, changelog heading, Git tag, and npm version must agree.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Local publication uses npm browser authentication. The GitHub Actions publish workflow is manual-only; pushing a tag does not publish the package.
|
|
6
6
|
|
|
7
7
|
## Prerequisites
|
|
8
8
|
|
|
9
9
|
- Write access to `hknet/pi-usage-bars`.
|
|
10
10
|
- Publish access to the npm scope `@hk_net`.
|
|
11
|
-
- npm authentication for the `hk_net` account (`npm whoami`).
|
|
12
11
|
- Node.js 22.19 or newer.
|
|
13
|
-
-
|
|
12
|
+
- A clean, synchronized `main` branch.
|
|
14
13
|
|
|
15
|
-
Never place an npm token in the repository, command history, or endpoint
|
|
14
|
+
Never place an npm token in the repository, command history, or endpoint configuration. Complete npm's browser or OTP authorization when prompted.
|
|
16
15
|
|
|
17
|
-
##
|
|
16
|
+
## Recommended release script
|
|
18
17
|
|
|
19
|
-
From the repository root:
|
|
18
|
+
From the repository root, provide the exact new semantic version:
|
|
20
19
|
|
|
21
20
|
```bash
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
git status --short
|
|
25
|
-
node -p "require('./package.json').version"
|
|
21
|
+
cd /path/to/pi-usage-bars
|
|
22
|
+
scripts/release.sh 0.4.1
|
|
26
23
|
```
|
|
27
24
|
|
|
28
|
-
|
|
25
|
+
This **default command only prepares the release**. It deliberately stops before npm login or publication, so the pushed release commit can be reviewed first. The launcher first executes an immutable temporary copy of itself, so editing the script while a release is running cannot corrupt that run.
|
|
29
26
|
|
|
30
|
-
|
|
27
|
+
Preparation:
|
|
31
28
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
Expected results for `0.4.0`:
|
|
29
|
+
1. verifies that `main` is clean and synchronized with `origin/main`;
|
|
30
|
+
2. rejects an existing npm version or Git tag;
|
|
31
|
+
3. updates `package.json` and `package-lock.json` without creating an early tag;
|
|
32
|
+
4. promotes the `Unreleased` changelog entries into a dated release while preserving an empty `Unreleased` heading;
|
|
33
|
+
5. installs dependencies with `npm ci --no-audit`, provisions a temporary pinned Bun binary, and runs typecheck, tests, detailed dependency audits, and package dry-run; and
|
|
34
|
+
6. commits and pushes the release source.
|
|
39
35
|
|
|
40
|
-
|
|
41
|
-
- 32 tests pass;
|
|
42
|
-
- Pi smoke test passes;
|
|
43
|
-
- production audit has no vulnerabilities;
|
|
44
|
-
- the dry-run package is `@hk_net/pi-usage-bars@0.4.0` and contains the extension, documentation, changelog, and license.
|
|
45
|
-
|
|
46
|
-
## 2. Verify npm state
|
|
36
|
+
After reviewing the prepared commit, stage it for browser approval:
|
|
47
37
|
|
|
48
38
|
```bash
|
|
49
|
-
|
|
50
|
-
npm view @hk_net/pi-usage-bars version
|
|
39
|
+
scripts/release.sh 0.4.1 stage
|
|
51
40
|
```
|
|
52
41
|
|
|
53
|
-
|
|
42
|
+
`stage` submits the package to npm's staged-publishing queue. It **does not publish live**, does not open a browser, and does not tag Git. If npm authentication is needed, run `npm login --auth-type=web` manually first.
|
|
54
43
|
|
|
55
|
-
|
|
44
|
+
In [npmjs.com](https://www.npmjs.com), open **Staged Packages**, review the staged tarball, and click **Approve**. npm prompts for 2FA during approval. After npm shows the version as live, finalize the release:
|
|
56
45
|
|
|
57
46
|
```bash
|
|
58
|
-
|
|
47
|
+
scripts/release.sh 0.4.1 finalize
|
|
59
48
|
```
|
|
60
49
|
|
|
61
|
-
|
|
50
|
+
`finalize` refuses to run until the exact version is live in the npm registry. It then verifies registry metadata and creates/pushes the annotated Git tag. It never stages or publishes a package.
|
|
62
51
|
|
|
63
|
-
|
|
52
|
+
Use `--yes` only for attended preparation:
|
|
64
53
|
|
|
65
54
|
```bash
|
|
66
|
-
|
|
67
|
-
npm publish --access public --provenance=false
|
|
55
|
+
scripts/release.sh 0.4.1 --yes # prepare only
|
|
68
56
|
```
|
|
69
57
|
|
|
70
|
-
|
|
58
|
+
## Recovery
|
|
59
|
+
|
|
60
|
+
Staging and finalization are safe to rerun: `stage` refuses if the exact version is already live, and `finalize` refuses until it is live. Never force-push a release tag.
|
|
61
|
+
|
|
62
|
+
The release script first prints a full dependency audit with package names, dependency paths, affected ranges, and available fixes. This is informational because it includes development-only dependencies. It then runs a blocking production-only audit, which rejects high or critical production findings.
|
|
71
63
|
|
|
72
|
-
|
|
64
|
+
Show script help with:
|
|
73
65
|
|
|
74
66
|
```bash
|
|
75
|
-
|
|
76
|
-
npm view @hk_net/pi-usage-bars@0.4.0 dist.integrity
|
|
67
|
+
scripts/release.sh --help
|
|
77
68
|
```
|
|
78
69
|
|
|
79
|
-
|
|
70
|
+
## Post-release verification
|
|
80
71
|
|
|
81
|
-
|
|
72
|
+
Confirm installation through Pi:
|
|
82
73
|
|
|
83
74
|
```bash
|
|
84
75
|
pi install npm:@hk_net/pi-usage-bars
|
|
85
|
-
# If
|
|
76
|
+
# If already installed from npm:
|
|
86
77
|
pi update npm:@hk_net/pi-usage-bars
|
|
87
78
|
```
|
|
88
79
|
|
|
89
80
|
Restart Pi, or use `/reload`, then run `/usage`.
|
|
90
81
|
|
|
91
|
-
##
|
|
92
|
-
|
|
93
|
-
Only tag after npm verification succeeds:
|
|
94
|
-
|
|
95
|
-
```bash
|
|
96
|
-
git switch main
|
|
97
|
-
git pull --ff-only origin main
|
|
98
|
-
git tag -a v0.4.0 -m "Release v0.4.0"
|
|
99
|
-
git push origin v0.4.0
|
|
100
|
-
```
|
|
101
|
-
|
|
102
|
-
The tag does not trigger npm publication. It records the exact source corresponding to the already-published package. A GitHub release can then be created from `v0.4.0` using the `0.4.0` changelog section.
|
|
103
|
-
|
|
104
|
-
## CI publishing alternative
|
|
82
|
+
## GitHub Actions alternative
|
|
105
83
|
|
|
106
|
-
The **Publish to npm (manual)**
|
|
84
|
+
The **Publish to npm (manual)** workflow is available as an alternative. It requires the `NPM_TOKEN` repository secret and publishes with npm provenance. Do not run it after publishing the same version locally.
|
|
@@ -56,6 +56,7 @@ export interface UsageData {
|
|
|
56
56
|
extraLimit?: number;
|
|
57
57
|
sessionLabel?: string;
|
|
58
58
|
weeklyLabel?: string;
|
|
59
|
+
sessionHidden?: boolean;
|
|
59
60
|
weeklyHidden?: boolean;
|
|
60
61
|
notice?: string;
|
|
61
62
|
warning?: string;
|
|
@@ -470,6 +471,7 @@ function snapshotUsage(usage: UsageData, nowMs = Date.now()): UsageData {
|
|
|
470
471
|
extraLimit: usage.extraLimit,
|
|
471
472
|
sessionLabel: usage.sessionLabel,
|
|
472
473
|
weeklyLabel: usage.weeklyLabel,
|
|
474
|
+
sessionHidden: usage.sessionHidden,
|
|
473
475
|
weeklyHidden: usage.weeklyHidden,
|
|
474
476
|
notice: usage.notice,
|
|
475
477
|
fetchedAt: usage.fetchedAt ?? nowMs,
|
|
@@ -520,6 +522,41 @@ function readClaudeCacheOutcome(cacheFile = DEFAULT_USAGE_CACHE_FILE, nowMs = Da
|
|
|
520
522
|
return null;
|
|
521
523
|
}
|
|
522
524
|
|
|
525
|
+
export function parseCodexRateLimit(data: any): UsageData {
|
|
526
|
+
const rateLimit = data?.rate_limit ?? data?.rate_limits;
|
|
527
|
+
const primary = rateLimit?.primary_window ?? rateLimit?.primary ?? rateLimit?.five_hour;
|
|
528
|
+
const secondary = rateLimit?.secondary_window ?? rateLimit?.secondary ?? rateLimit?.weekly;
|
|
529
|
+
|
|
530
|
+
let sessionWindow: any = null;
|
|
531
|
+
let weeklyWindow: any = null;
|
|
532
|
+
for (const [position, window] of [["primary", primary], ["secondary", secondary]] as const) {
|
|
533
|
+
if (!window || typeof window !== "object") continue;
|
|
534
|
+
const duration = window.limit_window_seconds;
|
|
535
|
+
if (typeof duration === "number" && Number.isFinite(duration)) {
|
|
536
|
+
// Some Codex accounts return their seven-day quota as primary_window
|
|
537
|
+
// and omit secondary_window, so position alone does not identify it.
|
|
538
|
+
if (duration >= 2 * 24 * 60 * 60) weeklyWindow ??= window;
|
|
539
|
+
else sessionWindow ??= window;
|
|
540
|
+
} else if (position === "primary") {
|
|
541
|
+
sessionWindow ??= window;
|
|
542
|
+
} else {
|
|
543
|
+
weeklyWindow ??= window;
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
const reset = (window: any) =>
|
|
548
|
+
typeof window?.reset_after_seconds === "number" ? formatDuration(window.reset_after_seconds) : undefined;
|
|
549
|
+
|
|
550
|
+
return {
|
|
551
|
+
session: readPercentCandidate(sessionWindow?.used_percent) ?? 0,
|
|
552
|
+
weekly: readPercentCandidate(weeklyWindow?.used_percent) ?? 0,
|
|
553
|
+
...(!sessionWindow ? { sessionHidden: true } : {}),
|
|
554
|
+
...(!weeklyWindow ? { weeklyHidden: true } : {}),
|
|
555
|
+
sessionResetsIn: reset(sessionWindow),
|
|
556
|
+
weeklyResetsIn: reset(weeklyWindow),
|
|
557
|
+
};
|
|
558
|
+
}
|
|
559
|
+
|
|
523
560
|
export async function fetchCodexUsage(token: string, config: RequestConfig = {}): Promise<UsageData> {
|
|
524
561
|
const result = await requestJson(
|
|
525
562
|
"https://chatgpt.com/backend-api/wham/usage",
|
|
@@ -527,16 +564,7 @@ export async function fetchCodexUsage(token: string, config: RequestConfig = {})
|
|
|
527
564
|
config,
|
|
528
565
|
);
|
|
529
566
|
if (!result.ok) return { session: 0, weekly: 0, error: result.error };
|
|
530
|
-
|
|
531
|
-
const data = result.data as any;
|
|
532
|
-
const primary = data?.rate_limit?.primary_window;
|
|
533
|
-
const secondary = data?.rate_limit?.secondary_window;
|
|
534
|
-
return {
|
|
535
|
-
session: readPercentCandidate(primary?.used_percent) ?? 0,
|
|
536
|
-
weekly: readPercentCandidate(secondary?.used_percent) ?? 0,
|
|
537
|
-
sessionResetsIn: typeof primary?.reset_after_seconds === "number" ? formatDuration(primary.reset_after_seconds) : undefined,
|
|
538
|
-
weeklyResetsIn: typeof secondary?.reset_after_seconds === "number" ? formatDuration(secondary.reset_after_seconds) : undefined,
|
|
539
|
-
};
|
|
567
|
+
return parseCodexRateLimit(result.data);
|
|
540
568
|
}
|
|
541
569
|
|
|
542
570
|
async function fetchClaudeUsageAttempt(
|
|
@@ -256,12 +256,14 @@ class UsageSelectorComponent extends Container implements Focusable {
|
|
|
256
256
|
const weeklyLabel = (item.data.weeklyLabel ?? "Weekly").slice(0, 9).padEnd(10);
|
|
257
257
|
|
|
258
258
|
if (!item.data.quotaHidden) {
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
theme.fg(
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
259
|
+
if (!item.data.sessionHidden) {
|
|
260
|
+
this.listContainer.addChild(new Text(
|
|
261
|
+
indent + theme.fg("muted", sessionLabel) + this.renderBar(session) + " " +
|
|
262
|
+
theme.fg(colorForPercent(session), `${session}%`.padStart(4)) + sessionReset,
|
|
263
|
+
0,
|
|
264
|
+
0,
|
|
265
|
+
));
|
|
266
|
+
}
|
|
265
267
|
if (!item.data.weeklyHidden) {
|
|
266
268
|
this.listContainer.addChild(new Text(
|
|
267
269
|
indent + theme.fg("muted", weeklyLabel) + this.renderBar(weekly) + " " +
|
|
@@ -459,15 +461,22 @@ export default function (pi: ExtensionAPI): void {
|
|
|
459
461
|
: data.sessionLabel === "Key limit"
|
|
460
462
|
? "L "
|
|
461
463
|
: "S ";
|
|
462
|
-
const
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
464
|
+
const quotaLanes: string[] = [];
|
|
465
|
+
if (!data.sessionHidden) {
|
|
466
|
+
quotaLanes.push(
|
|
467
|
+
theme.fg("muted", sessionPrefix) + renderBar(theme, session) + " " + renderPercent(theme, session) +
|
|
468
|
+
(data.sessionResetsIn ? theme.fg("dim", ` ⟳ ${data.sessionResetsIn}`) : ""),
|
|
469
|
+
);
|
|
470
|
+
}
|
|
471
|
+
if (!data.weeklyHidden) {
|
|
472
|
+
quotaLanes.push(
|
|
473
|
+
theme.fg("muted", "W ") + renderBar(theme, weekly) + " " + renderPercent(theme, weekly) +
|
|
474
|
+
(data.weeklyResetsIn ? theme.fg("dim", ` ⟳ ${data.weeklyResetsIn}`) : ""),
|
|
475
|
+
);
|
|
476
|
+
}
|
|
466
477
|
const status =
|
|
467
478
|
theme.fg("dim", `${label} `) +
|
|
468
|
-
|
|
469
|
-
(data.sessionResetsIn ? theme.fg("dim", ` ⟳ ${data.sessionResetsIn}`) : "") +
|
|
470
|
-
weeklyStatus +
|
|
479
|
+
quotaLanes.join(" ") +
|
|
471
480
|
(data.accountBalance ? theme.fg("muted", ` · ${formatAccountBalance(data.accountBalance)}`) : "") +
|
|
472
481
|
(data.accountSpend?.monthly === undefined
|
|
473
482
|
? ""
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hk_net/pi-usage-bars",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.1",
|
|
4
4
|
"description": "Quota, balance, and spend indicators for current Pi providers",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pi-package",
|
|
@@ -40,8 +40,7 @@
|
|
|
40
40
|
"scripts": {
|
|
41
41
|
"test": "bun test tests",
|
|
42
42
|
"typecheck": "tsc --noEmit",
|
|
43
|
-
"
|
|
44
|
-
"check": "npm run typecheck && npm test && npm run smoke",
|
|
43
|
+
"check": "npm run typecheck && npm test",
|
|
45
44
|
"prepublishOnly": "npm run check"
|
|
46
45
|
},
|
|
47
46
|
"engines": {
|
|
@@ -52,8 +51,8 @@
|
|
|
52
51
|
"@earendil-works/pi-tui": ">=0.81.1"
|
|
53
52
|
},
|
|
54
53
|
"devDependencies": {
|
|
55
|
-
"@earendil-works/pi-coding-agent": "0.
|
|
56
|
-
"@earendil-works/pi-tui": "0.
|
|
54
|
+
"@earendil-works/pi-coding-agent": "0.83.0",
|
|
55
|
+
"@earendil-works/pi-tui": "0.83.0",
|
|
57
56
|
"@types/bun": "1.3.14",
|
|
58
57
|
"@types/node": "24.13.3",
|
|
59
58
|
"typescript": "5.9.3"
|