@cad0p/pi-usage 0.0.0-stage → 0.11.2

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.
Files changed (43) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +509 -2
  3. package/bin/piquota.js +199 -0
  4. package/extensions/quota-panel.ts +963 -0
  5. package/package.json +59 -4
  6. package/src/auth/claude-code-auth.js +490 -0
  7. package/src/auth/jwt.js +84 -0
  8. package/src/auth/pi-auth.js +424 -0
  9. package/src/browser/cookies.js +557 -0
  10. package/src/browser/history.js +173 -0
  11. package/src/cache.js +224 -0
  12. package/src/cli/args.js +154 -0
  13. package/src/cli/commands/auth.js +216 -0
  14. package/src/cli/commands/gentle-pi.js +44 -0
  15. package/src/cli/commands/omarchy.js +75 -0
  16. package/src/cli/explain.js +99 -0
  17. package/src/cli/output.js +34 -0
  18. package/src/engine.js +404 -0
  19. package/src/exec.js +31 -0
  20. package/src/gentle-pi/rail-patch.js +335 -0
  21. package/src/http.js +182 -0
  22. package/src/model.js +332 -0
  23. package/src/omarchy/assets/antigravity.svg +3 -0
  24. package/src/omarchy/assets/claude.svg +1 -0
  25. package/src/omarchy/assets/codex.svg +1 -0
  26. package/src/omarchy/assets/github-actions.svg +3 -0
  27. package/src/omarchy/assets/opencode-go.svg +3 -0
  28. package/src/omarchy/publish.js +152 -0
  29. package/src/omarchy/record.js +462 -0
  30. package/src/opencode/dashboard.js +337 -0
  31. package/src/opencode/session.js +485 -0
  32. package/src/providers/antigravity-oauth.js +173 -0
  33. package/src/providers/antigravity.js +225 -0
  34. package/src/providers/backoff.js +146 -0
  35. package/src/providers/claude.js +194 -0
  36. package/src/providers/codex.js +151 -0
  37. package/src/providers/error-kind.js +128 -0
  38. package/src/providers/github-actions.js +379 -0
  39. package/src/providers/opencode-go.js +274 -0
  40. package/src/refresh.js +292 -0
  41. package/src/render/panel.js +411 -0
  42. package/src/render/theme.js +102 -0
  43. package/src/sticky.js +319 -0
package/bin/piquota.js ADDED
@@ -0,0 +1,199 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * piquota — independent, read-only quota for the credentials Pi already owns.
4
+ *
5
+ * piquota panel for Claude, Codex, Antigravity, OpenCode Go
6
+ * piquota --json normalized report (used by rp-host / tools)
7
+ * piquota --compact | --status | --explain
8
+ * piquota auth opencode open the OpenCode login and capture the session
9
+ * piquota auth status show which credential sources are reachable
10
+ * piquota omarchy publish pi-*.json records for Omarchy's Agents bar panel
11
+ * piquota gentle-pi <status|apply|revert>
12
+ * the rail slot gentle-pi needs to paint the quota card
13
+ */
14
+
15
+ import { clearCache, withCache } from "../src/cache.js";
16
+ import { parseArgs } from "../src/cli/args.js";
17
+ import { runAuthCommand } from "../src/cli/commands/auth.js";
18
+ import { runGentlePiCommand } from "../src/cli/commands/gentle-pi.js";
19
+ import { runOmarchyCommand } from "../src/cli/commands/omarchy.js";
20
+ import { explainLines, resolveFamilies } from "../src/cli/explain.js";
21
+ import { err, out } from "../src/cli/output.js";
22
+ import { collectQuota, enabledFamilies } from "../src/engine.js";
23
+ import { redact } from "../src/http.js";
24
+ import { mergeLastGood } from "../src/sticky.js";
25
+ import { renderBoxWidget, renderCompact, renderPanel, renderStatusLine } from "../src/render/panel.js";
26
+ import { ansiPalette } from "../src/render/theme.js";
27
+
28
+ const VERSION = "0.11.1";
29
+
30
+ const HELP = `piquota ${VERSION} — read-only quota from Pi's provider credentials
31
+
32
+ Usage:
33
+ piquota [families...] [flags]
34
+ piquota auth <opencode|status> [flags]
35
+ piquota gentle-pi <status|apply|revert>
36
+ piquota omarchy
37
+
38
+ Quota:
39
+ --json Emit the normalized report as JSON (for rp-host and automations)
40
+ --compact, -c One line per provider
41
+ --status Single line with rings
42
+ --box Compact right-aligned 5-row box widget
43
+ --no-color Disable ANSI colors
44
+ --no-cache Skip the local cache
45
+ --force Ignore a still-fresh cache entry
46
+ --ttl <seconds> Cache TTL (default 60)
47
+ --timeout <ms> Per-request timeout (default 15000)
48
+ --no-refresh Never refresh Antigravity's token in memory
49
+ --explain Show which stores/fields are read (names only)
50
+ --clear-cache Delete the local cache and exit
51
+ --version Print the version and exit
52
+
53
+ Auth (OpenCode Go session, the only credential Pi does not store):
54
+ piquota auth opencode open the login in Firefox and capture the cookie
55
+ piquota auth opencode --paste read the cookie from stdin instead
56
+ piquota auth opencode --no-browser
57
+ print the URL without launching a browser
58
+ piquota auth opencode --wait <s> how long to wait for the login (default 180)
59
+ piquota auth status report every credential source
60
+
61
+ Omarchy:
62
+ piquota omarchy write pi-<family>.json records for Omarchy's Agents bar panel
63
+ (dir: $PI_QUOTA_OMARCHY_DIR or $XDG_STATE_HOME/omarchy/agents/usage)
64
+ run it on a timer: ./install.sh --omarchy-timer
65
+
66
+ Guarantees:
67
+ * ~/.pi/agent/auth.json is opened read-only. Never written, synced or refreshed.
68
+ * Antigravity's access token may be refreshed in memory; it is never persisted.
69
+ * Browser cookie databases are copied and opened read-only; values are never logged.
70
+ * Only percentages, window labels, reset times and plan names leave this machine.
71
+ * \`omarchy\` (opt-in) writes only pi-*.json records, atomically, into Omarchy's usage dir;
72
+ records hold plan names, percentages, window labels and reset times, never accounts.
73
+ * \`gentle-pi\` is the one file this tool edits outside its own state: it appends
74
+ piQuota's part to gentle-pi's rail allowlist so the card can be painted there.
75
+ The edit is one array literal, it is reversible with \`piquota gentle-pi revert\`,
76
+ and the previous revision is kept next to the file as a \`.pi-quota-backup\`.
77
+ `;
78
+
79
+ async function main() {
80
+ const argv = process.argv.slice(2);
81
+ const args = parseArgs(argv);
82
+ const bare = args.positionals;
83
+ const command = bare[0] ?? null;
84
+ const agentMode = "native";
85
+ const refresh = !argv.includes("--no-refresh");
86
+
87
+ if (args.help) {
88
+ process.stdout.write(HELP);
89
+ return 0;
90
+ }
91
+ if (args.version) {
92
+ out(`piquota ${VERSION}`);
93
+ return 0;
94
+ }
95
+ if (args.clearCache) {
96
+ const result = clearCache({});
97
+ out(result.removed ? `cache cleared: ${result.path}` : `no cache at ${result.path}`);
98
+ return 0;
99
+ }
100
+
101
+ if (args.unknown.length > 0) {
102
+ err(`unknown flag: ${args.unknown.join(" ")}\n`);
103
+ process.stdout.write(HELP);
104
+ return 2;
105
+ }
106
+
107
+ if (command === "auth") {
108
+ const sub = bare[1] ?? "status";
109
+ return runAuthCommand(sub, argv);
110
+ }
111
+
112
+ if (command === "gentle-pi") {
113
+ const sub = bare[1] ?? "status";
114
+ return runGentlePiCommand(sub);
115
+ }
116
+
117
+ if (command === "omarchy") {
118
+ const load = async () => {
119
+ const everyFamily = enabledFamilies({ env: process.env });
120
+ const loadFresh = () => collectQuota({ families: everyFamily, timeoutMs: args.timeoutMs, refresh, force: args.force });
121
+ const fresh = args.noCache
122
+ ? await loadFresh()
123
+ : (await withCache({ ttlMs: args.ttlMs, force: args.force, families: everyFamily }, loadFresh)).report;
124
+ return mergeLastGood(fresh, {}).report;
125
+ };
126
+ return runOmarchyCommand({ load });
127
+ }
128
+
129
+ const { families, unknown, explicit } = resolveFamilies(
130
+ command === "quota" ? bare.slice(1) : bare.filter((value) => value !== "quota"),
131
+ );
132
+
133
+ if (unknown.length > 0) {
134
+ err(`unknown argument: ${unknown.join(" ")}\n`);
135
+ process.stdout.write(HELP);
136
+ return 2;
137
+ }
138
+
139
+ const selected = enabledFamilies({
140
+ env: process.env,
141
+ requested: families.length > 0 ? families : undefined,
142
+ explicit,
143
+ });
144
+ const load = () => collectQuota({ families: selected, explicit, timeoutMs: args.timeoutMs, refresh, force: args.force });
145
+
146
+ let report;
147
+ let cached = false;
148
+ let ageMs = 0;
149
+ if (args.noCache) {
150
+ report = await load();
151
+ } else {
152
+ const result = await withCache({ ttlMs: args.ttlMs, force: args.force, families: selected }, load);
153
+ report = result.report;
154
+ cached = result.cached;
155
+ ageMs = result.ageMs;
156
+ }
157
+
158
+ const { report: reportWithHistory, restored } = mergeLastGood(report, {});
159
+ report = reportWithHistory;
160
+ if (restored.length > 0 && !args.json && !args.compact && !args.status) {
161
+ process.stderr.write(`note: showing last known values for ${restored.join(", ")}\n`);
162
+ }
163
+
164
+ const paint = ansiPalette({ color: args.color });
165
+
166
+ if (args.json) {
167
+ out(JSON.stringify({ ...report, cache: { used: cached, ageMs } }, null, 2));
168
+ return report.providers.some((provider) => provider.ok) ? 0 : 1;
169
+ }
170
+ if (args.status) {
171
+ out(renderStatusLine(report.providers, paint));
172
+ return 0;
173
+ }
174
+ if (args.box) {
175
+ for (const line of renderBoxWidget(report.providers, paint)) out(line);
176
+ } else if (args.compact) {
177
+ for (const line of renderCompact(report.providers, paint)) out(line);
178
+ } else {
179
+ for (const line of renderPanel(report.providers, paint, {
180
+ generatedAt: report.generatedAt,
181
+ warnings: report.warnings,
182
+ })) {
183
+ out(line);
184
+ }
185
+ }
186
+ if (args.explain) {
187
+ for (const line of explainLines(selected)) out(line);
188
+ }
189
+
190
+ return report.providers.some((provider) => provider.ok) ? 0 : 1;
191
+ }
192
+
193
+ main().then(
194
+ (code) => process.exit(code),
195
+ (error) => {
196
+ err(`piquota failed: ${redact(/** @type {{ message?: string }} */ (error)?.message ?? error)}`);
197
+ process.exit(1);
198
+ },
199
+ );