@bacnh85/pi-sub 0.1.38 → 0.1.40
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 +31 -0
- package/README.md +1 -1
- package/extensions/index.ts +29 -9
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,36 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.1.40 (2026-09-14)
|
|
4
|
+
|
|
5
|
+
### Tests
|
|
6
|
+
|
|
7
|
+
- Added unit test coverage for the three untested parsers: the OmniRoute
|
|
8
|
+
om-usage free-text report (`parseOmniUsageText` — four windows, section
|
|
9
|
+
switching, out-of-range percentages, disabled/no-cache text), the Command
|
|
10
|
+
Code `/alpha/billing/credits` window mapper (used/cap → remaining%, epoch-ms
|
|
11
|
+
resetAt, over-cap clamp, bad-window bail), and the `.env.local` parser
|
|
12
|
+
(extracted as exported `parseEnvText`; `export ` prefix, quoted values,
|
|
13
|
+
comments, CRLF, `#`-in-value). No behavior changes.
|
|
14
|
+
One edge case differs from the inline loop it replaces: duplicate keys in a
|
|
15
|
+
single .env file now resolve last-wins (dotenv convention) instead of
|
|
16
|
+
first-wins.
|
|
17
|
+
|
|
18
|
+
## 0.1.39 (2026-09-12)
|
|
19
|
+
|
|
20
|
+
### Fixed
|
|
21
|
+
|
|
22
|
+
- **Guarded footer render against an uninitialized theme proxy** (pi-budget
|
|
23
|
+
parity): `renderSubscriptionLine` dereferenced `ctx.ui.theme.fg` unguarded —
|
|
24
|
+
if the theme isn't ready yet the throw escapes as a rejected promise and can
|
|
25
|
+
exit pi (the same unhandledRejection class 0.1.37/0.1.38 fixed elsewhere).
|
|
26
|
+
The footer is now best-effort: skipped when the theme isn't available.
|
|
27
|
+
- **Finite-cost guard on `message_end` accumulation** (pi-budget parity): a
|
|
28
|
+
string or NaN `cost.total` previously hit `+=` directly — a string cost
|
|
29
|
+
concatenated onto the accumulator and garbled every subsequent footer.
|
|
30
|
+
Costs are now coerced with `Number()` and only finite positive values
|
|
31
|
+
accumulate.
|
|
32
|
+
- README intro: Router (pi-router) listed among supported providers.
|
|
33
|
+
|
|
3
34
|
## 0.1.38 (2026-09-07)
|
|
4
35
|
|
|
5
36
|
### Fixed
|
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
|
-
Supports OpenAI Codex (`openai-codex`) with live usage windows from ChatGPT's usage endpoint, OpenCode Go (`opencode-go`) with session cost tracking, and Z.ai GLM Coding Plan — both the international (`zai`) and China (`zai-coding-cn`, `open.bigmodel.cn`) endpoints — with quota monitoring. Also tracks Command Code (`commandcode`) 5-hour/weekly windows and monthly credit balance. Displays a subscription footer status after Pi's built-in status/token usage line.
|
|
5
|
+
Supports OpenAI Codex (`openai-codex`) with live usage windows from ChatGPT's usage endpoint, OpenCode Go (`opencode-go`) with session cost tracking, and Z.ai GLM Coding Plan — both the international (`zai`) and China (`zai-coding-cn`, `open.bigmodel.cn`) endpoints — with quota monitoring. Also tracks Router (pi-router, `router` provider) with response-speed tracking and optional OmniRoute quota windows, and Command Code (`commandcode`) 5-hour/weekly windows and monthly credit balance. Displays a subscription footer status after Pi's built-in status/token usage line.
|
|
6
6
|
|
|
7
7
|
## Install
|
|
8
8
|
|
package/extensions/index.ts
CHANGED
|
@@ -4,6 +4,22 @@ import fs from "node:fs";
|
|
|
4
4
|
import os from "node:os";
|
|
5
5
|
import path from "node:path";
|
|
6
6
|
|
|
7
|
+
/** Parse .env-style text into KEY→VALUE entries: `export ` prefix allowed,
|
|
8
|
+
* single/double quotes stripped, comment/blank/non-assignment lines ignored.
|
|
9
|
+
* No inline-comment stripping (a `#` in the value stays part of the value).
|
|
10
|
+
* Exported for tests. */
|
|
11
|
+
export function parseEnvText(text: string): Record<string, string> {
|
|
12
|
+
const out: Record<string, string> = {};
|
|
13
|
+
for (const line of text.split(/\r?\n/)) {
|
|
14
|
+
const m = line.match(/^\s*(?:export\s+)?([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(.*)\s*$/);
|
|
15
|
+
if (!m) continue;
|
|
16
|
+
let v = m[2].trim();
|
|
17
|
+
if ((v.startsWith('"') && v.endsWith('"')) || (v.startsWith("'") && v.endsWith("'"))) v = v.slice(1, -1);
|
|
18
|
+
out[m[1]] = v;
|
|
19
|
+
}
|
|
20
|
+
return out;
|
|
21
|
+
}
|
|
22
|
+
|
|
7
23
|
/** Pi config dirs + .env.local/.env discovery (pi-munin convention, stdlib parse). */
|
|
8
24
|
function loadEnvFiles(): void {
|
|
9
25
|
const dirs = process.env.PI_CODING_AGENT_DIR
|
|
@@ -14,12 +30,8 @@ function loadEnvFiles(): void {
|
|
|
14
30
|
for (const file of candidates) {
|
|
15
31
|
try {
|
|
16
32
|
const text = fs.readFileSync(file, "utf8");
|
|
17
|
-
for (const
|
|
18
|
-
|
|
19
|
-
if (!m) continue;
|
|
20
|
-
let v = m[2].trim();
|
|
21
|
-
if ((v.startsWith('"') && v.endsWith('"')) || (v.startsWith("'") && v.endsWith("'"))) v = v.slice(1, -1);
|
|
22
|
-
if (process.env[m[1]] === undefined) process.env[m[1]] = v;
|
|
33
|
+
for (const [key, value] of Object.entries(parseEnvText(text))) {
|
|
34
|
+
if (process.env[key] === undefined) process.env[key] = value;
|
|
23
35
|
}
|
|
24
36
|
} catch { /* optional file */ }
|
|
25
37
|
}
|
|
@@ -437,7 +449,8 @@ export function parseOmniUsageText(text: string): {
|
|
|
437
449
|
return out;
|
|
438
450
|
}
|
|
439
451
|
|
|
440
|
-
// ponytail: runnable self-check (
|
|
452
|
+
// ponytail: runnable self-check (pack gate; extensions/test covers the same
|
|
453
|
+
// parser paths plus the adapters the self-check doesn't)
|
|
441
454
|
if (process.env.PI_SUB_SELF_CHECK === "1") {
|
|
442
455
|
const sample = [
|
|
443
456
|
"Personal quota", "Daily", "80% left", "⏱ reset in 15h 0m", "",
|
|
@@ -850,7 +863,7 @@ interface CommandCodeCreditsApiResponse {
|
|
|
850
863
|
|
|
851
864
|
/** Map a Command Code USD window (used/cap in dollars, resetAt in ms) into
|
|
852
865
|
* the shared UsageWindow shape (remaining%, reset labels). */
|
|
853
|
-
function commandCodeWindowToUsageWindow(window: CommandCodeWindowApi | undefined): UsageWindow | undefined {
|
|
866
|
+
export function commandCodeWindowToUsageWindow(window: CommandCodeWindowApi | undefined): UsageWindow | undefined {
|
|
854
867
|
if (!window || typeof window.used !== "number" || typeof window.cap !== "number" || window.cap <= 0) return undefined;
|
|
855
868
|
const usedPct = Math.round((window.used / window.cap) * 100);
|
|
856
869
|
const percent = Math.min(100, usedPct);
|
|
@@ -1103,6 +1116,9 @@ export function renderSubscriptionLine(state: State): void {
|
|
|
1103
1116
|
const ctx = state.ctx;
|
|
1104
1117
|
if (!ctx) return;
|
|
1105
1118
|
const theme = ctx.ui.theme;
|
|
1119
|
+
// pi-budget parity: the theme proxy may not be initialized yet — dereferencing
|
|
1120
|
+
// theme.fg throws (unhandledRejection → pi exits). Best-effort footer: skip.
|
|
1121
|
+
if (!theme?.fg) return;
|
|
1106
1122
|
if (!state.adapter) {
|
|
1107
1123
|
// Unsupported provider (e.g. Ollama): still show the last response speed.
|
|
1108
1124
|
ctx.ui.setStatus(STATUS_KEY, state.lastTokPerSec !== undefined ? theme.fg("dim", `${state.lastTokPerSec} tok/s`) : undefined);
|
|
@@ -1340,7 +1356,11 @@ export default function (pi: ExtensionAPI) {
|
|
|
1340
1356
|
|
|
1341
1357
|
pi.on("message_end", async (event, _ctx) => {
|
|
1342
1358
|
if (event.message.role === "assistant") {
|
|
1343
|
-
|
|
1359
|
+
// pi-budget parity: coerce + finite guard so a string/NaN cost.total can
|
|
1360
|
+
// never poison the accumulator (string concat garbles every subsequent
|
|
1361
|
+
// footer).
|
|
1362
|
+
const cost = Number((event.message.usage as any)?.cost?.total);
|
|
1363
|
+
if (Number.isFinite(cost) && cost > 0) state.cumulativeCost += cost;
|
|
1344
1364
|
if (state.responseStartTime) {
|
|
1345
1365
|
// usage.output already includes reasoning tokens (Pi SDK contract) —
|
|
1346
1366
|
// this is total tok/s in both thinking and normal mode.
|