@llblab/pi-codex-usage 0.10.1 → 0.12.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/AGENTS.md +9 -6
- package/BACKLOG.md +2 -1
- package/CHANGELOG.md +12 -1
- package/README.md +75 -17
- package/index.ts +8 -1602
- package/lib/extension.ts +25 -0
- package/lib/fast.ts +23 -0
- package/lib/query.ts +368 -0
- package/lib/status-format.ts +347 -0
- package/lib/status.ts +435 -0
- package/lib/telegram.ts +45 -0
- package/lib/usage-store.ts +229 -0
- package/lib/usage.ts +425 -0
- package/package.json +11 -6
package/AGENTS.md
CHANGED
|
@@ -1,17 +1,20 @@
|
|
|
1
1
|
# Agent Notes
|
|
2
2
|
|
|
3
|
-
- `
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
- `Registry dependency`: Use published `@llblab/pi-command-fast@^0.1.0` with aligned registry lock metadata. Local folder links are only for explicitly requested development tests; rebuild the library's `dist/` after source edits and restore registry dependency/locks before consumer publication.
|
|
4
|
+
- `Pi baseline`: Every declared `@earendil-works/*` peer requires ≥1.0.0. Keep Pi peer lock identities aligned and validate against that host generation.
|
|
5
|
+
- `Domain DAG`: `index.ts` only re-exports the extension and public contracts; `lib/extension.ts` composes the Pi extension. `lib/usage-store.ts` owns persistence/leadership, `lib/query.ts` owns quota I/O, `lib/usage.ts` owns normalized reports, `lib/status.ts` and `lib/status-format.ts` own local UI orchestration and formatting, `lib/telegram.ts` owns optional registration, and `lib/fast.ts` owns Codex Fast semantics/request adaptation. `@llblab/pi-command-fast` owns command arbitration and generic JSONC override editing. Keep dependency direction acyclic; do not import `index.ts` from `lib/` or mix Fast persistence into quota coordination. Place domain tests in `tests/<domain>.test.ts` (or a domain-prefixed focused integration test); use independent names such as `invariants.test.ts` only for cross-domain invariants.
|
|
6
|
+
- `Statusline-first scope`: Own usage state + usage mode: keep quota reporting zero-configuration and the optional Fast toggle focused on the existing terminal status.
|
|
7
|
+
- Trigger: Considering new commands, menus, persisted settings, or notification output.
|
|
8
|
+
- Action: Prefer deleting the surface unless it is required for the optimistic TUI status widget, the optional `pi-telegram` `/start` status-line mirror, or the existing argument-free `/fast` command. Register the `openai-codex` provider handler through `registerFastProvider` on session_start and release on session_shutdown; never register `/fast` directly or filter Fast by model ID/auth. Fast persists solely as `serviceTier: "priority"` in the current model override; OFF deletes only that property. Preserve JSONC/unrelated settings, existing wire tiers, and usage refresh/Telegram. Apply lowercase ` fast` through the final terminal boundary; toggle redraws without quota fetch or success notification. The library owns session WeakMap/reload arbitration, not Fast state. A Fast config read failure must not block usage status.
|
|
7
9
|
- `Optimistic refresh`: Preserve the last good statusline bar during refresh and transient failures.
|
|
8
10
|
- Trigger: Updating quota polling or error handling.
|
|
9
11
|
- Action: Do not collapse the bar while a request is in flight; only show `n/a` or `error` after repeated failures or no usable quota.
|
|
10
|
-
|
|
11
12
|
- `Adaptive compact status`: Match the status representation to the server-provided quota windows.
|
|
12
13
|
- Trigger: Changing statusline formatting.
|
|
13
14
|
- Action: When both windows exist, keep the classic dual bar with 20 top steps for the 5-hour window and 20 bottom steps for the weekly window. When only one weekly window exists, show its rounded remaining percentage directly instead of using a bar.
|
|
14
|
-
|
|
15
15
|
- `Weekly reset countdown`: Append the weekly reset countdown whenever the available weekly window exposes a reset time.
|
|
16
16
|
- Trigger: Changing reset-time normalization or statusline refresh cadence.
|
|
17
17
|
- Action: Treat the secondary window as weekly in dual-window responses and the sole window as weekly in single-window responses. Keep `d` labels rounded upward in 144-minute day-tenth steps above 24h, show 24h..1h labels in upward-rounded 6-minute hour-tenth steps, keep `m`/`s` labels floored, and hold `0s` until a successful quota refresh reports the next window.
|
|
18
|
+
- `Shared refresh`: Instances must not poll independently.
|
|
19
|
+
- Trigger: Changing refresh cadence, retries, locking, or adding fetch paths.
|
|
20
|
+
- Action: Keep the single shared-state protocol in `lib/usage-store.ts` (one shared `~/.pi/agent/tmp/pi-codex-usage/usage.json` for all Codex models; leader refreshes every minute, takeover after 90 seconds by claiming leadership before fetching, a non-waiting OS-backed SQLite mutex around claiming and fenced publication, atomic writes, shared failure backoff). Always re-read the file for request authorization (`owner` + `claimId` + lease) and publication; do not use an in-memory ownership fallback. Lock failures and failed claim writes must deny requests. `mutex.sqlite` stores no quota or leadership data: never unlink/replace it while instances run or evict a paused holder; close or process death releases the mutex. Keep network calls outside critical sections. Instances otherwise only read the file. Ignore additional quota buckets; do not restore model-specific labels, source priority, or cache directories. Keep the coordination behavior in sync with `pi-claude-usage`, which has its own independent state directory.
|
package/BACKLOG.md
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
# Backlog
|
|
2
2
|
|
|
3
|
-
No
|
|
3
|
+
- Operator-owned terminal/optional Telegram visual smoke remains separate from the packed offline Pi SDK smoke. No operator session reload was performed.
|
|
4
|
+
- Further paid live investigation needs fresh authorization: the earlier six-generation sample sent priority but the backend reported `response.service_tier: "default"`. Stored intent/terminal suffix are not service confirmation.
|
package/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,17 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
##
|
|
3
|
+
## 0.12.0: Shared Fast Mode and Pi 1.0 Baseline
|
|
4
|
+
|
|
5
|
+
- Requires Pi ≥1.0.0 across the coding-agent, AI and agent-core peers; previous hosts are outside this release's compatibility contract.
|
|
6
|
+
- Added argument-free, persistent per-model `/fast` for the `openai-codex` provider with no model-ID allowlist or Fast-specific auth gate. `@llblab/pi-command-fast@^0.1.0` coordinates one command alongside Claude Usage across duplicate library copies, separate sessions and reloads; generic JSONC editing moved to that normal library. Enabled stores only `serviceTier: "priority"`; OFF removes the property.
|
|
7
|
+
- Enabled Fast adds `service_tier: "priority"` to eligible Codex requests only when no tier is already present, and appends `fast` to the existing terminal status in the same dim theme color as its reset countdown, with immediate redraw and no success notification. State follows model selection and survives restart; the loading renderer tracks the newly selected model so an old timer cannot restore its previous suffix. Quota coordination and the Telegram status row are unchanged.
|
|
8
|
+
- Split the monolithic entrypoint into an explicit domain DAG under `lib/`. `index.ts` only re-exports the composition root in `lib/extension.ts` and previous public contracts. The quota store, provider transport, normalized report, status lifecycle/format, Telegram adapter, and Fast override have separate owners; tests now live in `tests/` with domain-matched names.
|
|
9
|
+
|
|
10
|
+
## 0.11.0: Shared Quota Refresh
|
|
11
|
+
|
|
12
|
+
- Coordinated polling across Pi instances through one `~/.pi/agent/tmp/pi-codex-usage/usage.json`: the leader refreshes every 60 seconds, followers normally read every 30 seconds, and leadership becomes eligible for takeover after 90 seconds without a touch. Failure backoff is shared, the last successful report stays visible for up to an hour, and provisional full-availability confirmation runs only in the refreshing instance.
|
|
13
|
+
- Made the JSON authoritative for request admission and fenced publication using the on-disk owner, claim generation, and lease. A non-waiting OS-backed SQLite mutex serializes claiming and publication; lock or claim-write failures deny requests, and late results cannot overwrite a successor. **Upgrade:** close all old Pi instances before starting updated ones; do not mix locking protocols or remove the mutex file while instances run.
|
|
14
|
+
- **Breaking:** Removed Spark-specific quotas, labels, and source priority. All Codex models now use the primary `codex` quota; additional buckets and legacy per-bucket caches are ignored without migration. Dual-bar rendering, loading animation, reset countdowns, Business credit usage, Pi auth, and the Codex app-server fallback are preserved.
|
|
4
15
|
|
|
5
16
|
## 0.10.1: Softer Quota Bar
|
|
6
17
|
|
package/README.md
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
# pi-codex-usage
|
|
2
2
|
|
|
3
|
-
>
|
|
3
|
+
> Pi extension for ChatGPT Codex usage limits and a local Fast toggle
|
|
4
4
|
|
|
5
5
|

|
|
6
6
|
|
|
7
|
+
This extension owns **usage state + usage mode**: zero-configuration quota reporting, plus an optional per-model Fast preference. Shared command arbitration and JSONC editing belong to [`@llblab/pi-command-fast`](https://github.com/llblab/pi-command-fast), a normal dependency, not another Pi extension.
|
|
8
|
+
|
|
7
9
|
This repository is a minimal fork of [`narumiruna/pi-extensions/extensions/pi-codex-usage`](https://github.com/narumiruna/pi-extensions/tree/main/extensions/pi-codex-usage). It keeps the auth and quota-fetching path, but intentionally narrows the interface to the Codex quota windows returned by OpenAI.
|
|
8
10
|
|
|
9
11
|
## Start Here
|
|
@@ -14,22 +16,24 @@ This repository is a minimal fork of [`narumiruna/pi-extensions/extensions/pi-co
|
|
|
14
16
|
|
|
15
17
|
## Features
|
|
16
18
|
|
|
17
|
-
- Shows two counter-moving half-height markers in the statusline bar while
|
|
18
|
-
- Keeps the last usable
|
|
19
|
+
- Shows two counter-moving half-height markers in the statusline bar while Codex usage is loading, then keeps the bar fresh (the countdown ticks locally)
|
|
20
|
+
- Keeps the last usable bar visible during ordinary refreshes instead of replacing known quota with a loading state
|
|
19
21
|
- Statusline output adapts to the response: weekly-only limits show an explicit remaining percentage and reset countdown, while dual-window limits keep the compact themed bar
|
|
20
|
-
-
|
|
21
|
-
- `
|
|
22
|
-
-
|
|
23
|
-
- Additional returned buckets unrelated to the active Codex/Spark model are ignored
|
|
22
|
+
- All Codex subscription models share the primary `codex` quota and status label
|
|
23
|
+
- When `pi-telegram` is available, the same compact value appears as `codex: <value>` in the `/start` menu status text for active OpenAI Codex subscription models
|
|
24
|
+
- Additional returned quota buckets are ignored
|
|
24
25
|
- Pi OpenAI Codex provider auth is used first
|
|
25
26
|
- Codex CLI app-server remains available as a fallback
|
|
26
27
|
- Missing auth, subscription, plan, or quota windows are shown as `n/a`, not as an error
|
|
27
28
|
- Successful updates briefly redraw the bar only when a 5% segment changes
|
|
28
|
-
- Network/provider failures keep the last good bar
|
|
29
|
-
-
|
|
29
|
+
- Network/provider failures keep the last good bar (up to an hour), then show `error`
|
|
30
|
+
- Any number of Pi instances share one request stream, see [Shared Refresh](#shared-refresh)
|
|
31
|
+
- Quota display requires no configuration; `/fast` optionally toggles priority service for the active native Codex model
|
|
30
32
|
|
|
31
33
|
## Install
|
|
32
34
|
|
|
35
|
+
Requires Pi ≥1.0.0 and Node ≥22.19.0. Every declared Pi package peer follows the 1.0.0 minimum.
|
|
36
|
+
|
|
33
37
|
From npm:
|
|
34
38
|
|
|
35
39
|
```bash
|
|
@@ -42,18 +46,53 @@ From git:
|
|
|
42
46
|
pi install git:github.com/llblab/pi-codex-usage
|
|
43
47
|
```
|
|
44
48
|
|
|
45
|
-
##
|
|
49
|
+
## Development
|
|
46
50
|
|
|
47
|
-
|
|
51
|
+
The shared `@llblab/pi-command-fast@^0.1.0` dependency now resolves from npm; no sibling library checkout is required.
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
npm ci
|
|
55
|
+
npm run validate
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
For explicitly local library experiments, a temporary folder link reads the library's built `dist/`, not TypeScript source directly. Rebuild after source edits and restore the registry dependency/lock before publishing; see [Backlog](./BACKLOG.md).
|
|
59
|
+
|
|
60
|
+
## Fast mode
|
|
61
|
+
|
|
62
|
+
`/fast` takes no arguments and dispatches by the current **provider**, not model names or OAuth eligibility. For any `openai-codex` model, it stores only `serviceTier: "priority"` in Pi's canonical `models.json` (honoring `PI_CODING_AGENT_DIR`):
|
|
63
|
+
|
|
64
|
+
```json
|
|
65
|
+
{
|
|
66
|
+
"providers": {
|
|
67
|
+
"openai-codex": {
|
|
68
|
+
"modelOverrides": {
|
|
69
|
+
"your-current-model": { "serviceTier": "priority" }
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
OFF removes only `serviceTier`; it never writes `"default"`. JSONC comments and unrelated configuration survive. There is no separate Fast config or model allowlist. State follows provider/model selection and survives restart; manual edits are read on lifecycle refresh and each request.
|
|
77
|
+
|
|
78
|
+
When this and Claude Usage are loaded, their shared library registers **one** `/fast`, in either load order, even with separate physical dependency copies. A session-scoped WeakMap avoids cross-session dispatch; shutdown releases registrations for reload (Pi retains the session manager). Unrelated providers receive a concise unsupported-provider message. Invalid arguments show `Usage: /fast`; success is silent.
|
|
79
|
+
|
|
80
|
+
Enabled native requests receive `service_tier: "priority"` only when the payload matches the current model and has no existing tier. The existing **terminal** status redraws immediately without fetching quota, for example:
|
|
48
81
|
|
|
49
82
|
```text
|
|
50
|
-
codex ██████▀▀▀▀ 6d
|
|
83
|
+
codex ██████▀▀▀▀ 6d fast
|
|
51
84
|
```
|
|
52
85
|
|
|
53
|
-
|
|
86
|
+
The lowercase ` fast` suffix uses the existing dim/countdown theme role and is applied at the final terminal boundary, including loading, percentages/credits, `n/a`, and errors. Telegram values and quota polling/auth/leadership are unchanged.
|
|
87
|
+
|
|
88
|
+
Pi 1.0.0 accepts the extra override but does not propagate it to native request options, so a small `before_provider_request` adapter remains necessary; no replacement provider or transport is registered. Its public command API cannot hide/unregister commands by current model, so `/fast` stays listed and checks the provider at invocation. Backend capability and actual priority service are not guaranteed by a stored preference or suffix: an earlier authorized sample sent `priority` but received `default`. Priority service may have different provider pricing.
|
|
89
|
+
|
|
90
|
+
## Statusline
|
|
91
|
+
|
|
92
|
+
Regular Codex usage:
|
|
54
93
|
|
|
55
94
|
```text
|
|
56
|
-
|
|
95
|
+
codex ██████▀▀▀▀ 6d
|
|
57
96
|
```
|
|
58
97
|
|
|
59
98
|
When OpenAI returns only the weekly window, the status shows the exact rounded remaining percentage and reset countdown directly:
|
|
@@ -64,7 +103,7 @@ codex 67% 7d
|
|
|
64
103
|
|
|
65
104
|
When both windows exist, the ten-character bar encodes two twenty-step limits at once: the top quadrants show the 5-hour limit and the bottom quadrants show the weekly limit, with each step representing 5%. If either quota window is exhausted, the bar keeps its shape but switches to the error background color.
|
|
66
105
|
|
|
67
|
-
Before the first usable report
|
|
106
|
+
Before the first usable Codex report arrives, two half-height markers move through the same fixed-width themed bar. The upper 5-hour marker travels opposite the lower weekly marker; both reverse smoothly at the ends, and each loader run randomly starts from one of the two mirrored endpoint phases. Their motion distinguishes loading from 100% remaining quota while preserving the normal bar background. A first report claiming both windows are completely unused is treated as provisional for 15 seconds and retried every second by the refreshing instance before it is published, because providers can briefly emit zeroed windows while initializing. Once a usable report exists, refresh requests preserve that last good bar.
|
|
68
107
|
|
|
69
108
|
When the weekly reset time is available, it follows either the single-window percentage or the dual-window bar. More than a day remains is shown in 144-minute day-tenth steps such as `7d`, `6.9d`, `6.6d`, `5.1d`, `5d`, `3.7d`, `3d`, `2d`, `1.9d`, `1.5d`, and `1.1d`, rounded upward to the next tenth. At 24 hours and below it switches to upward-rounded 6-minute hour-tenth steps such as `24h`, `23.7h`, `20.1h`, `20h`, `19.9h`, `1.4h`, `1.3h`, `1.2h`, `1.1h`, and `1h`. Under an hour it switches to floored minutes, and under a minute to seconds. After the reset timestamp passes, `0s` is held until the next successful quota refresh reports the new weekly window.
|
|
70
109
|
|
|
@@ -94,16 +133,35 @@ Runtime failure, such as a network or provider error:
|
|
|
94
133
|
codex error
|
|
95
134
|
```
|
|
96
135
|
|
|
136
|
+
## Shared Refresh
|
|
137
|
+
|
|
138
|
+
[`index.ts`](./index.ts) is a re-export-only Pi entrypoint; [`lib/extension.ts`](./lib/extension.ts) composes the live extension. [`lib/usage-store.ts`](./lib/usage-store.ts) owns cross-instance quota coordination, [`lib/query.ts`](./lib/query.ts) owns provider requests, [`lib/usage.ts`](./lib/usage.ts) owns quota normalization, [`lib/status.ts`](./lib/status.ts) and [`lib/status-format.ts`](./lib/status-format.ts) own lifecycle and terminal display, [`lib/telegram.ts`](./lib/telegram.ts) adapts the optional Telegram row, and [`lib/fast.ts`](./lib/fast.ts) owns Codex Fast semantics and the native payload bridge; the shared library owns JSONC and command arbitration. Domain tests live under [`tests/`](./tests/) with matching names; integration tests name the domain whose boundary they exercise.
|
|
139
|
+
|
|
140
|
+
All Codex models and instances coordinate through `~/.pi/agent/tmp/pi-codex-usage/usage.json` (quota percentages and timestamps only, no tokens). The Claude extension uses its own independent file at `~/.pi/agent/tmp/pi-claude-usage/usage.json`.
|
|
141
|
+
|
|
142
|
+
Previous per-bucket cache directories are no longer read or written; there is no migration. Restart all previously running instances when upgrading to stop them writing the old layout.
|
|
143
|
+
|
|
144
|
+
- The instance that last updated the file is the leader and refreshes it every minute
|
|
145
|
+
- Every other instance only reads the file (re-checking every ≤30s) and redraws when it changes
|
|
146
|
+
- Leadership is never cached in memory: before each usage request (including retries and fallbacks), the instance re-reads the file and checks its `owner`, unique `claimId`, and 90s lease. Publication rechecks that claim under the lock; a superseded success or failure is discarded. Failed locks or unwritten claims never authorize a request
|
|
147
|
+
- If the file is 90 seconds old (leader is closed, busy, or asleep), the first follower that obtains the mutex takes over: it re-reads the JSON, writes itself as the leader with a fresh timestamp, releases the mutex, then fetches from the server and publishes under the mutex after rechecking its claim. Its next refresh is one minute later; JSON writes remain atomic renames
|
|
148
|
+
- Claiming and publication use a non-waiting transaction in `mutex.sqlite`, through Node's built-in `node:sqlite` (Node ≥22.19.0, the existing package minimum). It stores no quota or leadership records and needs no extra package or service. The OS releases the lock when the connection closes or the process dies; there is no timeout-based lock stealing
|
|
149
|
+
- Failures are written to the file with an exponential backoff (1 to 5 minutes; 5 to 30 minutes on HTTP 429) that applies to all instances
|
|
150
|
+
- Instances without data yet show the loading bar and re-check every second until the leader publishes
|
|
151
|
+
|
|
152
|
+
Coordination assumes a local filesystem and cooperating instances on the same machine. Never delete or replace `mutex.sqlite` while instances are running. Network requests do not hold the mutex; a process paused inside a short critical section keeps it until it resumes or exits, so other writers retry later without blocking the TUI. This preserves exclusion instead of stealing a live lock.
|
|
153
|
+
|
|
154
|
+
**Upgrade:** Close all old instances before starting updated ones. The legacy `lock` files are ignored; old and new locking protocols must not run together. Cached `usage.json` data needs no migration.
|
|
155
|
+
|
|
97
156
|
## Telegram Status Menu
|
|
98
157
|
|
|
99
158
|
If `@llblab/pi-telegram` is loaded with the public status-line provider API, this extension registers an optional `/start` menu status row. The row is shown only while the active model uses the OpenAI Codex subscription provider:
|
|
100
159
|
|
|
101
160
|
```text
|
|
102
161
|
codex: ██████▀▀▀▀ 6d
|
|
103
|
-
spark: ██████████ 7d
|
|
104
162
|
```
|
|
105
163
|
|
|
106
|
-
The value is the same compact quota bar plus weekly reset countdown used by the terminal statusline,
|
|
164
|
+
The value is the same compact quota bar plus weekly reset countdown used by the terminal statusline, always with the `codex` label. If `pi-telegram` is absent, older, or the active model is not a Codex subscription model, no Telegram row is added.
|
|
107
165
|
|
|
108
166
|
## Auth
|
|
109
167
|
|