agy-cli-usage 0.3.0 → 0.4.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.
@@ -0,0 +1,169 @@
1
+ #!/usr/bin/env node
2
+ // agy-usage — Antigravity CLI (agy) usage/quota monitor.
3
+ //
4
+ // Usage:
5
+ // agy-usage one-shot panel (like agy's /usage)
6
+ // agy-usage --json machine-readable JSON
7
+ // agy-usage --watch [secs] refresh every N seconds (default 60)
8
+ // agy-usage --source api|pty|auto data source (default auto: api, fall back to pty)
9
+ // agy-usage --channel daily|prod Cloud Code host (default: auto-detect)
10
+ // agy-usage --no-cache bypass the 5-minute cache
11
+ // agy-usage --refresh force a fresh fetch (alias for --no-cache)
12
+ // agy-usage update [--check] self-update via npm
13
+ // agy-usage --version | -v print the installed version
14
+ import { getAccessToken, CredentialError } from './credentials.js';
15
+ import { fetchQuotaSummary } from './api.js';
16
+ import { captureUsageViaPty } from './pty-fallback.js';
17
+ import { fromApi, fromPty } from './quota.js';
18
+ import { renderPanel } from './render.js';
19
+ import { currentVersion, runUpdate } from './update.js';
20
+ import { readFileSync, writeFileSync, mkdirSync } from 'node:fs';
21
+ import { homedir } from 'node:os';
22
+ import { join } from 'node:path';
23
+ const CACHE_DIR = join(process.env.XDG_CACHE_HOME || join(homedir(), '.cache'), 'agy-usage');
24
+ const CACHE_FILE = join(CACHE_DIR, 'quota.json');
25
+ const CACHE_TTL_MS = 5 * 60 * 1000;
26
+ const errMessage = (e) => (e instanceof Error ? e.message : String(e));
27
+ function parseArgs(argv) {
28
+ const o = {
29
+ json: false, watch: null, source: 'auto', channel: 'auto', cache: true, command: null, check: false,
30
+ };
31
+ for (let i = 0; i < argv.length; i++) {
32
+ const a = argv[i];
33
+ if (a === 'update' && o.command == null)
34
+ o.command = 'update';
35
+ else if (a === '--json')
36
+ o.json = true;
37
+ else if (a === '--watch') {
38
+ const n = Number(argv[i + 1]);
39
+ if (Number.isFinite(n)) {
40
+ o.watch = n;
41
+ i++;
42
+ }
43
+ else
44
+ o.watch = 60;
45
+ }
46
+ else if (a === '--source')
47
+ o.source = (argv[++i] ?? 'auto');
48
+ else if (a === '--channel')
49
+ o.channel = (argv[++i] ?? 'auto');
50
+ else if (a === '--no-cache' || a === '--refresh')
51
+ o.cache = false;
52
+ else if (a === '--check')
53
+ o.check = true;
54
+ else if (a === '-v' || a === '--version')
55
+ o.version = true;
56
+ else if (a === '-h' || a === '--help')
57
+ o.help = true;
58
+ }
59
+ return o;
60
+ }
61
+ const HELP = `agy-usage — Antigravity CLI (agy) usage/quota monitor
62
+
63
+ agy-usage one-shot panel
64
+ agy-usage --json machine-readable JSON
65
+ agy-usage --watch [secs] auto-refresh (default 60s)
66
+ agy-usage --source <auto|api|pty>
67
+ agy-usage --channel <auto|daily|prod>
68
+ agy-usage --no-cache | --refresh
69
+ agy-usage update [--check] self-update via npm (--check: report only)
70
+ agy-usage --version | -v
71
+ `;
72
+ // --- cache -------------------------------------------------------------------
73
+ function readCache() {
74
+ try {
75
+ const { ts, snap } = JSON.parse(readFileSync(CACHE_FILE, 'utf8'));
76
+ if (Date.now() - ts < CACHE_TTL_MS)
77
+ return snap;
78
+ }
79
+ catch {
80
+ /* no/expired cache */
81
+ }
82
+ return null;
83
+ }
84
+ function writeCache(snap) {
85
+ try {
86
+ mkdirSync(CACHE_DIR, { recursive: true });
87
+ writeFileSync(CACHE_FILE, JSON.stringify({ ts: Date.now(), snap }));
88
+ }
89
+ catch {
90
+ /* cache is best-effort */
91
+ }
92
+ }
93
+ export async function getSnapshot(opts) {
94
+ if (opts.cache && opts.source !== 'pty') {
95
+ const cached = readCache();
96
+ if (cached)
97
+ return cached;
98
+ }
99
+ let snap;
100
+ if (opts.source === 'pty') {
101
+ snap = fromPty(await captureUsageViaPty());
102
+ }
103
+ else {
104
+ try {
105
+ const { accessToken } = await getAccessToken();
106
+ const raw = await fetchQuotaSummary(accessToken, { channel: opts.channel === 'auto' ? undefined : opts.channel });
107
+ snap = fromApi(raw);
108
+ }
109
+ catch (err) {
110
+ if (opts.source === 'api')
111
+ throw err;
112
+ // auto: fall back to PTY
113
+ process.stderr.write(`[api failed: ${errMessage(err)}] falling back to PTY (agy)…\n`);
114
+ snap = fromPty(await captureUsageViaPty());
115
+ }
116
+ }
117
+ writeCache(snap);
118
+ return snap;
119
+ }
120
+ // --- main --------------------------------------------------------------------
121
+ async function once(opts) {
122
+ const snap = await getSnapshot(opts);
123
+ if (opts.json)
124
+ process.stdout.write(JSON.stringify(snap, null, 2) + '\n');
125
+ else
126
+ process.stdout.write(renderPanel(snap) + '\n');
127
+ }
128
+ async function main() {
129
+ const opts = parseArgs(process.argv.slice(2));
130
+ if (opts.help) {
131
+ process.stdout.write(HELP);
132
+ return;
133
+ }
134
+ if (opts.version) {
135
+ process.stdout.write(currentVersion() + '\n');
136
+ return;
137
+ }
138
+ if (opts.command === 'update') {
139
+ process.exit(await runUpdate({ checkOnly: opts.check }));
140
+ }
141
+ if (opts.watch != null) {
142
+ const intervalMs = Math.max(5, opts.watch) * 1000;
143
+ const tick = async () => {
144
+ try {
145
+ if (!opts.json)
146
+ process.stdout.write('\x1b[2J\x1b[H'); // clear screen
147
+ await once(opts);
148
+ }
149
+ catch (err) {
150
+ process.stderr.write(`error: ${errMessage(err)}\n`);
151
+ }
152
+ };
153
+ await tick();
154
+ setInterval(tick, intervalMs);
155
+ }
156
+ else {
157
+ await once(opts);
158
+ }
159
+ }
160
+ main().catch((err) => {
161
+ if (err instanceof CredentialError) {
162
+ process.stderr.write(`credential error: ${err.message}\n`);
163
+ }
164
+ else {
165
+ process.stderr.write(`error: ${errMessage(err)}\n`);
166
+ }
167
+ process.exit(1);
168
+ });
169
+ //# sourceMappingURL=main.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"main.js","sourceRoot":"","sources":["../../src/main.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ import type { ParsedPanel } from './types.js';
2
+ /** Parse the reconstructed /usage screen text into { account, groups:[...] }. */
3
+ export declare function parsePanel(text: string): ParsedPanel;
4
+ /** Run agy, capture /usage, reconstruct + parse the panel. */
5
+ export declare function captureUsageViaPty(): Promise<ParsedPanel>;
@@ -0,0 +1,221 @@
1
+ // Fallback path: drive the real `agy` TUI in a pseudo-terminal, send `/usage`,
2
+ // reconstruct the rendered screen with a headless VT emulator, and parse the
3
+ // panel. Used only when the direct API path is unavailable (no readable
4
+ // keyring, or the internal API changed). Slower and more brittle than the API
5
+ // path, but uses agy's own auth so it works wherever agy itself works.
6
+ //
7
+ // Why a VT emulator: agy renders /usage in the alternate screen buffer using
8
+ // cursor addressing, so naive ANSI-stripping yields nothing. We feed the raw
9
+ // PTY bytes through @xterm/headless to get the final visible screen, then parse.
10
+ //
11
+ // Capture backend: python3 `pty` on POSIX (no native build), node-pty on
12
+ // Windows (ConPTY). agy shows a welcome screen first, so `/usage` is sent after
13
+ // a delay and the session is held open long enough to render.
14
+ import { spawn } from 'node:child_process';
15
+ import { writeFileSync, readFileSync, mkdtempSync, existsSync } from 'node:fs';
16
+ import { tmpdir, homedir } from 'node:os';
17
+ import { join, delimiter } from 'node:path';
18
+ // Resolve the agy binary: explicit AGY_BIN, then PATH, then common install dir.
19
+ function resolveAgy() {
20
+ const explicit = process.env.AGY_BIN;
21
+ if (explicit)
22
+ return explicit;
23
+ const exe = process.platform === 'win32' ? 'agy.exe' : 'agy';
24
+ for (const dir of (process.env.PATH || '').split(delimiter)) {
25
+ if (dir && existsSync(join(dir, exe)))
26
+ return join(dir, exe);
27
+ }
28
+ const local = join(homedir(), '.local', 'bin', exe);
29
+ if (existsSync(local))
30
+ return local;
31
+ return 'agy'; // last resort: let the OS resolve it
32
+ }
33
+ const AGY_BIN = resolveAgy();
34
+ const COLS = 120;
35
+ const ROWS = 60;
36
+ const USAGE_AT_MS = 10_000; // send /usage after the welcome screen settles
37
+ const TEARDOWN_MS = 23_000; // keep session open long enough to render
38
+ // --- capture: returns raw PTY bytes (Buffer) or null --------------------------
39
+ async function captureViaNodePty() {
40
+ // node-pty is an optional dependency; defeat static module resolution so the
41
+ // build doesn't require it (CI installs with --omit=optional).
42
+ const moduleName = 'node-pty';
43
+ let pty;
44
+ try {
45
+ pty = await import(moduleName);
46
+ }
47
+ catch {
48
+ return null;
49
+ }
50
+ return new Promise((resolve) => {
51
+ let term;
52
+ try {
53
+ term = pty.spawn(AGY_BIN, [], { name: 'xterm-256color', cols: COLS, rows: ROWS, cwd: process.cwd(), env: process.env });
54
+ }
55
+ catch {
56
+ resolve(null);
57
+ return;
58
+ }
59
+ const chunks = [];
60
+ term.onData((d) => chunks.push(Buffer.from(d, 'utf8')));
61
+ const t1 = setTimeout(() => { try {
62
+ term.write('/usage\r');
63
+ }
64
+ catch { /* ignore */ } }, USAGE_AT_MS);
65
+ const t2 = setTimeout(() => {
66
+ try {
67
+ term.write('\x03');
68
+ }
69
+ catch { /* ignore */ }
70
+ try {
71
+ term.kill();
72
+ }
73
+ catch { /* ignore */ }
74
+ resolve(Buffer.concat(chunks));
75
+ }, TEARDOWN_MS);
76
+ term.onExit(() => { clearTimeout(t1); clearTimeout(t2); resolve(Buffer.concat(chunks)); });
77
+ });
78
+ }
79
+ async function captureViaPython() {
80
+ if (process.platform === 'win32')
81
+ return null;
82
+ const dir = mkdtempSync(join(tmpdir(), 'agy-usage-'));
83
+ const helper = join(dir, 'drive.py');
84
+ const outFile = join(dir, 'out.bin');
85
+ writeFileSync(helper, `import os, pty, time, select, signal, struct, fcntl, termios
86
+ AGY = ${JSON.stringify(AGY_BIN)}
87
+ out = open(${JSON.stringify(outFile)}, "wb")
88
+ pid, fd = pty.fork()
89
+ if pid == 0:
90
+ os.execvpe(AGY, [AGY], os.environ)
91
+ os._exit(127)
92
+ fcntl.ioctl(fd, termios.TIOCSWINSZ, struct.pack("HHHH", ${ROWS}, ${COLS}, 0, 0))
93
+ start = time.time(); sent = False
94
+ while time.time() - start < ${TEARDOWN_MS / 1000}:
95
+ e = time.time() - start
96
+ r, _, _ = select.select([fd], [], [], 0.5)
97
+ if r:
98
+ try: d = os.read(fd, 8192)
99
+ except OSError: break
100
+ if not d: break
101
+ out.write(d); out.flush()
102
+ if not sent and e > ${USAGE_AT_MS / 1000}:
103
+ os.write(fd, b"/usage\\r"); sent = True
104
+ try: os.write(fd, b"\\x03")
105
+ except OSError: pass
106
+ try: os.kill(pid, signal.SIGTERM)
107
+ except Exception: pass
108
+ out.close()
109
+ `);
110
+ return new Promise((resolve) => {
111
+ const proc = spawn('python3', [helper], { stdio: 'ignore' });
112
+ proc.on('error', () => resolve(null));
113
+ proc.on('exit', () => {
114
+ try {
115
+ resolve(readFileSync(outFile));
116
+ }
117
+ catch {
118
+ resolve(null);
119
+ }
120
+ });
121
+ });
122
+ }
123
+ // --- VT reconstruction --------------------------------------------------------
124
+ async function reconstructScreen(raw) {
125
+ const { Terminal } = await import('@xterm/headless');
126
+ const term = new Terminal({ cols: COLS, rows: ROWS, allowProposedApi: true, scrollback: 200 });
127
+ await new Promise((res) => term.write(raw, res));
128
+ const buf = term.buffer.active;
129
+ const lines = [];
130
+ // include scrollback so a panel taller than the viewport is still captured
131
+ for (let i = 0; i < buf.length; i++) {
132
+ const line = buf.getLine(i);
133
+ if (line)
134
+ lines.push(line.translateToString(true).replace(/\s+$/, ''));
135
+ }
136
+ term.dispose();
137
+ return lines.join('\n');
138
+ }
139
+ // --- parse --------------------------------------------------------------------
140
+ function parseDuration(text) {
141
+ let seconds = 0;
142
+ const d = text.match(/(\d+)\s*day/i);
143
+ const h = text.match(/(\d+)\s*h(?:our)?/i);
144
+ const m = text.match(/(\d+)\s*m(?:in)?/i);
145
+ if (d)
146
+ seconds += +d[1] * 86400;
147
+ if (h)
148
+ seconds += +h[1] * 3600;
149
+ if (m)
150
+ seconds += +m[1] * 60;
151
+ return seconds || null;
152
+ }
153
+ /** Parse the reconstructed /usage screen text into { account, groups:[...] }. */
154
+ export function parsePanel(text) {
155
+ const lines = text.split(/\r?\n/);
156
+ const account = text.match(/Account:\s*(\S+)/)?.[1] ?? null;
157
+ const groups = [];
158
+ let group = null;
159
+ let bucket = null;
160
+ const pushBucket = () => { if (group && bucket)
161
+ group.buckets.push(bucket); bucket = null; };
162
+ const pushGroup = () => { pushBucket(); if (group)
163
+ groups.push(group); group = null; };
164
+ for (const line of lines) {
165
+ const t = line.trim();
166
+ if (!t)
167
+ continue;
168
+ if (/^[A-Z][A-Z0-9 &/]*MODELS$/.test(t)) {
169
+ pushGroup();
170
+ group = { name: t.replace(/\s+/g, ' '), models: '', buckets: [] };
171
+ continue;
172
+ }
173
+ const models = t.match(/^Models within this group:\s*(.+)$/i);
174
+ if (models && group) {
175
+ group.models = models[1].trim();
176
+ continue;
177
+ }
178
+ if (/^(Weekly Limit|Five Hour Limit|5[- ]?Hour Limit)$/i.test(t)) {
179
+ pushBucket();
180
+ const kind = /week/i.test(t) ? 'weekly' : '5h';
181
+ bucket = { kind, label: t, remainingFraction: null, resetsInSeconds: null, available: false, description: null };
182
+ continue;
183
+ }
184
+ if (bucket) {
185
+ const pct = t.match(/(\d+(?:\.\d+)?)\s*%/);
186
+ if (pct && bucket.remainingFraction == null)
187
+ bucket.remainingFraction = +pct[1] / 100;
188
+ if (/Quota available/i.test(t)) {
189
+ bucket.available = true;
190
+ bucket.remainingFraction = 1;
191
+ }
192
+ const refresh = t.match(/Refreshes in (.+)$/i);
193
+ if (refresh)
194
+ bucket.resetsInSeconds = parseDuration(refresh[1]);
195
+ }
196
+ }
197
+ pushGroup();
198
+ return { account, groups };
199
+ }
200
+ /** Run agy, capture /usage, reconstruct + parse the panel. */
201
+ export async function captureUsageViaPty() {
202
+ const order = process.platform === 'win32'
203
+ ? [captureViaNodePty, captureViaPython]
204
+ : [captureViaPython, captureViaNodePty];
205
+ let raw = null;
206
+ for (const fn of order) {
207
+ raw = await fn();
208
+ if (raw && raw.length)
209
+ break;
210
+ }
211
+ if (!raw || !raw.length) {
212
+ throw new Error('No PTY backend captured agy output (need python3 on POSIX, or node-pty on Windows)');
213
+ }
214
+ const screen = await reconstructScreen(raw);
215
+ const parsed = parsePanel(screen);
216
+ if (!parsed.groups.length) {
217
+ throw new Error('Could not parse /usage panel from agy output');
218
+ }
219
+ return parsed;
220
+ }
221
+ //# sourceMappingURL=pty-fallback.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pty-fallback.js","sourceRoot":"","sources":["../../src/pty-fallback.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ import type { FetchResult, ParsedPanel, Snapshot } from './types.js';
2
+ /** Build a normalized snapshot from the raw retrieveUserQuotaSummary response. */
3
+ export declare function fromApi({ raw, host, account, tier }: FetchResult, nowMs?: number): Snapshot;
4
+ /** Build a normalized snapshot from PTY-parsed groups (see pty-fallback.ts). */
5
+ export declare function fromPty(parsed: ParsedPanel, nowMs?: number): Snapshot;
6
+ /** Format a seconds duration like agy: "73h 53m" / "2h 7m" / "12m". */
7
+ export declare function formatDuration(seconds: number | null): string | null;
@@ -0,0 +1,81 @@
1
+ // Normalizes quota data from either source (direct API JSON or PTY-parsed text)
2
+ // into one shape consumed by the renderer / JSON output / HTTP endpoint.
3
+ function bucketKind(window, label) {
4
+ if (window === 'weekly' || /week/i.test(label))
5
+ return 'weekly';
6
+ if (window === '5h' || /5.?hour|five.?hour/i.test(label))
7
+ return '5h';
8
+ return window || label;
9
+ }
10
+ function secondsUntil(resetAt, now) {
11
+ if (!resetAt)
12
+ return null;
13
+ const ms = new Date(resetAt).getTime() - now;
14
+ return Number.isFinite(ms) ? Math.max(0, Math.round(ms / 1000)) : null;
15
+ }
16
+ /** Build a normalized snapshot from the raw retrieveUserQuotaSummary response. */
17
+ export function fromApi({ raw, host, account, tier }, nowMs = Date.now()) {
18
+ const groups = (raw.groups ?? []).map((g) => ({
19
+ name: g.displayName ?? 'Models',
20
+ models: (g.description ?? '').replace(/^Models within this group:\s*/i, '').trim(),
21
+ buckets: (g.buckets ?? []).map((b) => {
22
+ const remaining = typeof b.remainingFraction === 'number' ? b.remainingFraction : null;
23
+ return {
24
+ kind: bucketKind(b.window, b.displayName ?? ''),
25
+ label: b.displayName ?? b.window ?? '',
26
+ remainingFraction: remaining,
27
+ usedFraction: remaining == null ? null : 1 - remaining,
28
+ resetAt: b.resetTime ?? null,
29
+ resetsInSeconds: secondsUntil(b.resetTime, nowMs),
30
+ available: remaining === 1,
31
+ description: b.description ?? null,
32
+ };
33
+ }),
34
+ }));
35
+ return {
36
+ account: account ?? null,
37
+ tier: tier ?? null,
38
+ fetchedAt: new Date(nowMs).toISOString(),
39
+ source: 'api',
40
+ host: host ?? null,
41
+ note: raw.description ?? null,
42
+ groups,
43
+ };
44
+ }
45
+ /** Build a normalized snapshot from PTY-parsed groups (see pty-fallback.ts). */
46
+ export function fromPty(parsed, nowMs = Date.now()) {
47
+ const groups = (parsed.groups ?? []).map((g) => ({
48
+ name: g.name,
49
+ models: g.models ?? '',
50
+ buckets: (g.buckets ?? []).map((b) => ({
51
+ kind: b.kind,
52
+ label: b.label,
53
+ remainingFraction: b.remainingFraction ?? null,
54
+ usedFraction: b.remainingFraction == null ? null : 1 - b.remainingFraction,
55
+ resetAt: b.resetsInSeconds != null ? new Date(nowMs + b.resetsInSeconds * 1000).toISOString() : null,
56
+ resetsInSeconds: b.resetsInSeconds ?? null,
57
+ available: b.available ?? b.remainingFraction === 1,
58
+ description: b.description ?? null,
59
+ })),
60
+ }));
61
+ return {
62
+ account: parsed.account ?? null,
63
+ tier: null,
64
+ fetchedAt: new Date(nowMs).toISOString(),
65
+ source: 'pty',
66
+ host: null,
67
+ note: parsed.note ?? null,
68
+ groups,
69
+ };
70
+ }
71
+ /** Format a seconds duration like agy: "73h 53m" / "2h 7m" / "12m". */
72
+ export function formatDuration(seconds) {
73
+ if (seconds == null)
74
+ return null;
75
+ const h = Math.floor(seconds / 3600);
76
+ const m = Math.floor((seconds % 3600) / 60);
77
+ if (h > 0)
78
+ return `${h}h ${m}m`;
79
+ return `${m}m`;
80
+ }
81
+ //# sourceMappingURL=quota.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"quota.js","sourceRoot":"","sources":["../../src/quota.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { Snapshot } from './types.js';
2
+ /** Returns the full panel as a string. */
3
+ export declare function renderPanel(snap: Snapshot): string;
@@ -0,0 +1,82 @@
1
+ // Renders a normalized quota snapshot as a terminal panel, mirroring agy's
2
+ // `/usage` layout (progress bar + percent + reset time per bucket).
3
+ import { formatDuration } from './quota.js';
4
+ const BAR_WIDTH = 50;
5
+ const useColor = () => Boolean(process.stdout.isTTY) && !process.env.NO_COLOR;
6
+ const c = (code, s) => (useColor() ? `\x1b[${code}m${s}\x1b[0m` : s);
7
+ const dim = (s) => c('2', s);
8
+ const bold = (s) => c('1', s);
9
+ // remaining-based color: lots left = green, getting low = yellow/red.
10
+ function barColor(remaining) {
11
+ if (remaining == null)
12
+ return '37';
13
+ if (remaining > 0.5)
14
+ return '32'; // green
15
+ if (remaining > 0.2)
16
+ return '33'; // yellow
17
+ return '31'; // red
18
+ }
19
+ function bar(remainingFraction) {
20
+ const frac = remainingFraction == null ? 0 : Math.max(0, Math.min(1, remainingFraction));
21
+ const filled = Math.round(frac * BAR_WIDTH);
22
+ const body = '█'.repeat(filled) + '░'.repeat(BAR_WIDTH - filled);
23
+ return useColor() ? `\x1b[${barColor(remainingFraction)}m${body}\x1b[0m` : body;
24
+ }
25
+ function bucketLine(b) {
26
+ const lines = [];
27
+ lines.push(` ${bold(b.label)}`);
28
+ if (b.available) {
29
+ lines.push(` [${bar(1)}] ${c('32', 'Quota available')}`);
30
+ }
31
+ else {
32
+ const pct = b.remainingFraction == null ? '—' : `${(b.remainingFraction * 100).toFixed(2)}%`;
33
+ const remainPct = b.remainingFraction == null ? '' : `${Math.round(b.remainingFraction * 100)}% remaining`;
34
+ const dur = formatDuration(b.resetsInSeconds);
35
+ const reset = dur ? ` · ${dim(`Refreshes in ${dur}`)}` : '';
36
+ lines.push(` [${bar(b.remainingFraction)}] ${pct}`);
37
+ lines.push(` ${dim(remainPct)}${reset}`);
38
+ }
39
+ return lines.join('\n');
40
+ }
41
+ /** Returns the full panel as a string. */
42
+ export function renderPanel(snap) {
43
+ const out = [];
44
+ out.push('');
45
+ out.push(bold(' Models & Quota'));
46
+ if (snap.account)
47
+ out.push(` ${dim('Account:')} ${snap.account}`);
48
+ out.push(` ${dim(`source: ${snap.source}${snap.host ? ` · ${snap.host}` : ''} · ${snap.fetchedAt}`)}`);
49
+ out.push('');
50
+ for (const g of snap.groups) {
51
+ out.push(bold(` ${g.name.toUpperCase()}`));
52
+ if (g.models)
53
+ out.push(` ${dim(`Models within this group: ${g.models}`)}`);
54
+ out.push('');
55
+ for (const b of g.buckets) {
56
+ out.push(bucketLine(b));
57
+ out.push('');
58
+ }
59
+ }
60
+ if (snap.note) {
61
+ out.push(dim(wrap(snap.note, 76, ' │')));
62
+ }
63
+ return out.join('\n');
64
+ }
65
+ function wrap(text, width, prefix) {
66
+ const words = text.split(/\s+/);
67
+ const lines = [];
68
+ let cur = '';
69
+ for (const w of words) {
70
+ if ((cur + ' ' + w).trim().length > width) {
71
+ lines.push(prefix + cur);
72
+ cur = w;
73
+ }
74
+ else {
75
+ cur = (cur + ' ' + w).trim();
76
+ }
77
+ }
78
+ if (cur)
79
+ lines.push(prefix + cur);
80
+ return lines.join('\n');
81
+ }
82
+ //# sourceMappingURL=render.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render.js","sourceRoot":"","sources":["../../src/render.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};