pi-hypercharm-provider 1.1.5 → 1.2.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/.pi/fabric/mesh/state.json +106 -0
- package/AGENTS.md +2 -1
- package/README.md +52 -7
- package/deprecated-models.json +34 -1
- package/index.ts +936 -265
- package/models.json +49 -48
- package/package.json +9 -2
- package/pnpm-workspace.yaml +15 -0
- package/scripts/update-models.js +108 -2
- package/status.ts +305 -0
- package/tests/status.smoke.ts +146 -0
- package/tsconfig.json +15 -0
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dependency-free smoke test for the footer-status presentation module.
|
|
3
|
+
* Run: node tests/status.smoke.ts (Node ≥ 23 strips types natively).
|
|
4
|
+
* Exercises tier building, width math, widget layout, and config coercion —
|
|
5
|
+
* the pieces where a regression would silently corrupt the footer line.
|
|
6
|
+
*/
|
|
7
|
+
import assert from "node:assert/strict";
|
|
8
|
+
import {
|
|
9
|
+
EMPTY_ACCOUNT,
|
|
10
|
+
StatusLineWidget,
|
|
11
|
+
accountHasData,
|
|
12
|
+
buildAccountTiers,
|
|
13
|
+
buildSessionLine,
|
|
14
|
+
coerceStatusConfig,
|
|
15
|
+
formatBalHc,
|
|
16
|
+
formatRateCompact,
|
|
17
|
+
formatSpendHc,
|
|
18
|
+
termVisWidth,
|
|
19
|
+
truncateAnsi,
|
|
20
|
+
type AccountState,
|
|
21
|
+
} from "../status.ts";
|
|
22
|
+
|
|
23
|
+
const stripAnsi = (s: string) => s.replace(/\x1b\[[0-9;]*m/g, "");
|
|
24
|
+
const fakeTheme = { fg: (_c: string, t: string) => `\x1b[2m${t}\x1b[39m` };
|
|
25
|
+
|
|
26
|
+
// ── formatters ──
|
|
27
|
+
assert.equal(formatBalHc(249), "249");
|
|
28
|
+
assert.equal(formatBalHc(12345), "12.3k");
|
|
29
|
+
assert.equal(formatBalHc(250.5), "250.5");
|
|
30
|
+
assert.equal(formatBalHc(1_250_000), "1.25M");
|
|
31
|
+
assert.equal(formatSpendHc(0), "0");
|
|
32
|
+
assert.equal(formatSpendHc(0.0004), "~0");
|
|
33
|
+
assert.equal(formatSpendHc(0.0021), "0.0021");
|
|
34
|
+
assert.equal(formatSpendHc(0.31), "0.31");
|
|
35
|
+
assert.equal(formatSpendHc(12.5), "12.5");
|
|
36
|
+
assert.equal(formatRateCompact(996), "996");
|
|
37
|
+
assert.equal(formatRateCompact(1000), "1k");
|
|
38
|
+
assert.equal(formatRateCompact(9996), "10k");
|
|
39
|
+
|
|
40
|
+
// ── session line ──
|
|
41
|
+
assert.equal(buildSessionLine({ requests: 0, spendHc: 0 }), undefined);
|
|
42
|
+
assert.equal(buildSessionLine({ requests: 7, spendHc: 1.24 }), "⚡ 1.24 hc · 7 req");
|
|
43
|
+
assert.equal(buildSessionLine({ requests: 1, spendHc: 0 }), "⚡ 0 hc · 1 req");
|
|
44
|
+
|
|
45
|
+
// ── account tiers ──
|
|
46
|
+
const acc = (over: Partial<AccountState>): AccountState => ({ ...EMPTY_ACCOUNT, ...over });
|
|
47
|
+
const rate = { limitHour: 1000, limitDay: 10000, remainingHour: 996, remainingDay: 9996, capturedAt: 0 };
|
|
48
|
+
|
|
49
|
+
assert.equal(accountHasData(acc({})), false);
|
|
50
|
+
assert.equal(accountHasData(acc({ balance: 0 })), true);
|
|
51
|
+
|
|
52
|
+
const full = acc({ balance: 249, teamName: "ACME Team", rate, authDaysLeft: 29 });
|
|
53
|
+
const tiers = buildAccountTiers(full, false);
|
|
54
|
+
assert.equal(tiers[0], "ACME Team ◆ 249 hc · 996/1k/h · 10k/10k/d · ⟳ 29d");
|
|
55
|
+
assert.ok(tiers.includes("ACME Team ◆ 249 hc"));
|
|
56
|
+
assert.ok(tiers.includes("◆ 249 hc · 996/1k/h"));
|
|
57
|
+
assert.ok(tiers.includes("249 hc"));
|
|
58
|
+
// tiers must be strictly non-increasing in width
|
|
59
|
+
for (let i = 1; i < tiers.length; i++) {
|
|
60
|
+
assert.ok(termVisWidth(tiers[i]) <= termVisWidth(tiers[i - 1]), `tier ${i} wider than previous`);
|
|
61
|
+
}
|
|
62
|
+
// dedupe of adjacent identical tiers when atoms are missing
|
|
63
|
+
assert.deepEqual(buildAccountTiers(acc({ teamName: "ACME" }), false), ["ACME"]);
|
|
64
|
+
const balOnly = buildAccountTiers(acc({ balance: 12 }), true);
|
|
65
|
+
assert.equal(balOnly[0], "⚠ ◆ 12 hc");
|
|
66
|
+
assert.ok(balOnly.includes("12 hc"));
|
|
67
|
+
|
|
68
|
+
// ── width math ──
|
|
69
|
+
assert.equal(termVisWidth("abc"), 3);
|
|
70
|
+
assert.equal(termVisWidth(""), 0);
|
|
71
|
+
assert.equal(termVisWidth(fakeTheme.fg("dim", "abc")), 3, "ANSI is zero-width");
|
|
72
|
+
assert.equal(termVisWidth("◆"), 1, "◆ counts as narrow in this terminal");
|
|
73
|
+
assert.equal(termVisWidth("⚡"), 2);
|
|
74
|
+
assert.equal(truncateAnsi("hello world", 8), "hello w…");
|
|
75
|
+
assert.equal(termVisWidth(truncateAnsi(fakeTheme.fg("x", "hello world"), 8)), 8);
|
|
76
|
+
assert.equal(truncateAnsi("abc", 5), "abc");
|
|
77
|
+
assert.equal(truncateAnsi("abc", 0), "");
|
|
78
|
+
|
|
79
|
+
// ── widget render ──
|
|
80
|
+
const left = buildSessionLine({ requests: 7, spendHc: 1.24 })!;
|
|
81
|
+
const widget = new StatusLineWidget(fakeTheme, left, tiers, false);
|
|
82
|
+
|
|
83
|
+
// Wide: full tier, left-right justified, exactly width columns
|
|
84
|
+
const wide = widget.render(80);
|
|
85
|
+
assert.equal(wide.length, 1);
|
|
86
|
+
assert.equal(termVisWidth(wide[0]), 80);
|
|
87
|
+
assert.ok(stripAnsi(wide[0]).startsWith("⚡ 1.24 hc"));
|
|
88
|
+
assert.ok(stripAnsi(wide[0]).endsWith("⟳ 29d"));
|
|
89
|
+
|
|
90
|
+
// Medium: drops to a compressed tier, still exactly width
|
|
91
|
+
const med = widget.render(52);
|
|
92
|
+
assert.equal(termVisWidth(med[0]), 52);
|
|
93
|
+
assert.ok(!stripAnsi(med[0]).includes("⟳"), "compressed tiers drop auth first");
|
|
94
|
+
|
|
95
|
+
// Narrow: no tier fits → left only, padded
|
|
96
|
+
const narrow = widget.render(termVisWidth(left) + 3);
|
|
97
|
+
assert.equal(termVisWidth(narrow[0]), termVisWidth(left) + 3);
|
|
98
|
+
assert.ok(stripAnsi(narrow[0]).startsWith("⚡"));
|
|
99
|
+
assert.ok(!stripAnsi(narrow[0]).includes("◆"));
|
|
100
|
+
|
|
101
|
+
// Narrower than left itself: truncation never overflows (crash guard)
|
|
102
|
+
const tiny = widget.render(10);
|
|
103
|
+
assert.equal(termVisWidth(tiny[0]), 10);
|
|
104
|
+
|
|
105
|
+
// Left empty (session gated) → right-aligned account line
|
|
106
|
+
const rightOnly = new StatusLineWidget(fakeTheme, "", tiers, false);
|
|
107
|
+
const ro = rightOnly.render(70);
|
|
108
|
+
assert.equal(termVisWidth(ro[0]), 70);
|
|
109
|
+
assert.ok(stripAnsi(ro[0]).endsWith("⟳ 29d"));
|
|
110
|
+
|
|
111
|
+
// No data at all
|
|
112
|
+
assert.deepEqual(new StatusLineWidget(fakeTheme, "", [], false).render(40), [fakeTheme.fg("dim", "") + " ".repeat(40)]);
|
|
113
|
+
|
|
114
|
+
// Warning color wired through
|
|
115
|
+
const warn = new StatusLineWidget(fakeTheme, "", buildAccountTiers(acc({ balance: 10 }), true), true);
|
|
116
|
+
const warnLine = warn.render(60)[0];
|
|
117
|
+
assert.ok(stripAnsi(warnLine).includes("⚠ ◆ 10 hc"));
|
|
118
|
+
assert.ok(warnLine.includes("warning") || true); // fakeTheme ignores color names
|
|
119
|
+
const markTheme = { fg: (c: string, t: string) => `<${c}>${t}</>` };
|
|
120
|
+
assert.ok(new StatusLineWidget(markTheme, "", buildAccountTiers(acc({ balance: 10 }), true), true).render(60)[0].includes("<warning>"));
|
|
121
|
+
assert.ok(new StatusLineWidget(markTheme, "", buildAccountTiers(acc({ balance: 10 }), true), false).render(60)[0].includes("<dim>"));
|
|
122
|
+
|
|
123
|
+
// ── config coercion ──
|
|
124
|
+
assert.deepEqual(coerceStatusConfig(undefined), {
|
|
125
|
+
session: "widget",
|
|
126
|
+
account: "widget",
|
|
127
|
+
hideOnOtherProvider: true,
|
|
128
|
+
lowBalanceHc: 25,
|
|
129
|
+
});
|
|
130
|
+
assert.deepEqual(coerceStatusConfig({ session: "bogus", lowBalanceHc: -3 }), {
|
|
131
|
+
session: "widget",
|
|
132
|
+
account: "widget",
|
|
133
|
+
hideOnOtherProvider: true,
|
|
134
|
+
lowBalanceHc: 25,
|
|
135
|
+
});
|
|
136
|
+
assert.deepEqual(coerceStatusConfig({ session: "statusbar", account: "off", hideOnOtherProvider: false, lowBalanceHc: null }), {
|
|
137
|
+
session: "statusbar",
|
|
138
|
+
account: "off",
|
|
139
|
+
hideOnOtherProvider: false,
|
|
140
|
+
lowBalanceHc: null,
|
|
141
|
+
});
|
|
142
|
+
assert.equal(coerceStatusConfig({ lowBalanceHc: 42 }).lowBalanceHc, 42);
|
|
143
|
+
assert.equal(coerceStatusConfig({ lowBalanceHc: false }).lowBalanceHc, null);
|
|
144
|
+
assert.deepEqual(coerceStatusConfig(null).session, "widget");
|
|
145
|
+
|
|
146
|
+
console.log("status.smoke: all assertions passed");
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "ESNext",
|
|
5
|
+
"moduleResolution": "bundler",
|
|
6
|
+
"lib": ["ES2022", "DOM"],
|
|
7
|
+
"esModuleInterop": true,
|
|
8
|
+
"strict": false,
|
|
9
|
+
"skipLibCheck": true,
|
|
10
|
+
"noEmit": true,
|
|
11
|
+
"resolveJsonModule": true
|
|
12
|
+
},
|
|
13
|
+
"include": ["./*.ts"],
|
|
14
|
+
"exclude": ["node_modules", "tests"]
|
|
15
|
+
}
|