@bacnh85/pi-sub 0.1.39 → 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 CHANGED
@@ -1,5 +1,20 @@
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
+
3
18
  ## 0.1.39 (2026-09-12)
4
19
 
5
20
  ### Fixed
@@ -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 line of text.split(/\r?\n/)) {
18
- const m = line.match(/^\s*(?:export\s+)?([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(.*)\s*$/);
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 (pi-sub has no test runner pack gate only)
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);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bacnh85/pi-sub",
3
- "version": "0.1.39",
3
+ "version": "0.1.40",
4
4
  "description": "Pi extension showing subscription usage for supported model providers.",
5
5
  "type": "module",
6
6
  "license": "MIT",