@astrosheep/pi-quota 0.5.2 → 0.5.3

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@astrosheep/pi-quota",
3
- "version": "0.5.2",
3
+ "version": "0.5.3",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -4,21 +4,29 @@ import type { Paint, Span } from './bar.ts';
4
4
 
5
5
  export function money(amount: number, currency: string): string {
6
6
  const sign = amount < 0 ? '-' : '';
7
- const prefix = currency === 'USD' ? '$' : `${currency} `;
7
+ const symbol = currency === 'CNY' ? '¥' : currency === 'USD' ? '$' : `${currency} `;
8
8
  const absolute = Math.abs(amount);
9
- if (absolute > 0 && absolute < 0.01) return `${sign}<${prefix}0.01`;
10
- return `${sign}${prefix}${absolute.toFixed(2)}`;
9
+ if (absolute > 0 && absolute < 0.01) return `${sign}<${symbol}0.01`;
10
+ return `${sign}${symbol}${absolute.toFixed(2)}`;
11
11
  }
12
12
 
13
13
  export function remainingMoney(balance: AccountBalance): string {
14
- if (balance.unlimited === true) return balance.currency === 'USD' ? '$∞' : `${balance.currency} ∞`;
14
+ if (balance.unlimited === true) return balance.currency === 'CNY' ? '¥∞' : balance.currency === 'USD' ? '$∞' : `${balance.currency} ∞`;
15
15
  return money(balance.remaining, balance.currency);
16
16
  }
17
17
 
18
+ export function compactMoney(amount: number, currency: string): string {
19
+ return money(amount, currency);
20
+ }
21
+
22
+ export function compactRemainingMoney(balance: AccountBalance): string {
23
+ return remainingMoney(balance);
24
+ }
25
+
18
26
  export function balanceSpans(balance: AccountBalance): Span[] {
19
27
  return [
20
28
  { text: 'Bal ', tone: 'dim' },
21
- { text: remainingMoney(balance), tone: !balance.unlimited && balance.remaining <= 0 ? 'error' : 'text' },
29
+ { text: compactRemainingMoney(balance), tone: !balance.unlimited && balance.remaining <= 0 ? 'error' : 'text' },
22
30
  ];
23
31
  }
24
32
 
@@ -35,7 +43,7 @@ export function allowanceSpans(allowance: QuotaAllowance): Span[] {
35
43
  const remaining = allowancePercent(allowance);
36
44
  return [
37
45
  { text: 'Quota ', tone: 'dim' },
38
- { text: `${verticalBar(remaining)} ${money(allowance.remaining, allowance.currency)}`, tone: remainingTone(remaining) },
46
+ { text: `${verticalBar(remaining)} ${compactMoney(allowance.remaining, allowance.currency)}`, tone: remainingTone(remaining) },
39
47
  ];
40
48
  }
41
49
 
@@ -52,6 +60,5 @@ export function allowanceLines(allowance: QuotaAllowance, paint: Paint, barWidth
52
60
  export function spendLines(spend: SpendSummary, paint: Paint): string[] {
53
61
  const lines: string[] = [];
54
62
  if (spend.today !== undefined) lines.push(paint('dim', `Today ${money(spend.today, spend.currency)}`));
55
- if (spend.lifetime !== undefined) lines.push(paint('dim', `Lifetime ${money(spend.lifetime, spend.currency)}`));
56
63
  return lines;
57
64
  }
package/src/bar/card.ts CHANGED
@@ -38,7 +38,7 @@ export function usageCardComponent(card: UsageCard | undefined, paint?: Paint):
38
38
  return new Text('Usage snapshot unavailable.', 0, 0).render(width);
39
39
  }
40
40
  // Rebuild on every render for theme/width changes, but freeze time at capture.
41
- const lines = usageLines(card, paint, width < 55 ? 10 : 20);
41
+ const lines = usageLines(card, paint, width < 55 ? 10 : 18);
42
42
  // Wrapping, not clipping: long quota names remain readable in narrow terminals.
43
43
  return new Text(lines.join('\n'), 0, 0).render(width);
44
44
  },
package/src/bar/quota.ts CHANGED
@@ -46,16 +46,14 @@ export function renderFooter(state: QuotaState, paint: Paint = plain, now = Date
46
46
  return renderBar([quotaElement(state, now)], paint);
47
47
  }
48
48
 
49
- export function renderUsage(state: QuotaState, paint: Paint = plain, now = Date.now(), barWidth = 20): string[] {
49
+ const displayLabel: Record<string, string> = { deepseek: 'DeepSeek' };
50
+
51
+ export function renderUsage(state: QuotaState, paint: Paint = plain, now = Date.now(), barWidth = 16): string[] {
50
52
  if (state.kind === 'hidden') return ['No quota adapter for the current provider.'];
51
- const balanceOnly = state.kind === 'ready' && state.snapshot.balance
52
- && !state.snapshot.allowance && state.snapshot.windows.length === 0;
53
- const spendOnly = state.kind === 'ready' && state.snapshot.spend
54
- && !state.snapshot.balance && !state.snapshot.allowance && state.snapshot.windows.length === 0;
55
- const title = `${state.label} · ${balanceOnly ? (state.snapshot.spend ? 'Account' : 'Balance')
56
- : spendOnly ? 'Usage' : 'Remaining quota'}`;
57
- if (state.kind === 'loading') return [title, '', 'Loading…'];
58
- if (state.kind === 'error') return [title, '', errorText[state.code]];
53
+ const title = displayLabel[state.provider] ?? state.label;
54
+ const indent = (line: string) => line ? ` ${line}` : line;
55
+ if (state.kind === 'loading') return [title, '', indent('Loading…')];
56
+ if (state.kind === 'error') return [title, '', indent(errorText[state.code])];
59
57
  const rows = state.snapshot.windows.map(window => {
60
58
  const remaining = window.resetAt !== null && window.resetAt <= now ? null : window.remainingPercent;
61
59
  return {
@@ -69,26 +67,25 @@ export function renderUsage(state: QuotaState, paint: Paint = plain, now = Date.
69
67
  const labelWidth = Math.max(0, ...rows.map(row => visibleWidth(row.label)));
70
68
  const valueWidth = Math.max(4, ...rows.map(row => visibleWidth(row.value)));
71
69
  const lines = [title, '', ...rows.map(row => {
72
- const label = row.label + ' '.repeat(labelWidth - visibleWidth(row.label));
73
- const value = ' '.repeat(valueWidth - visibleWidth(row.value)) + row.value;
74
70
  const tone = remainingTone(row.remaining);
75
- return paint('dim', `${label} `)
71
+ const value = ' '.repeat(valueWidth - visibleWidth(row.value)) + row.value;
72
+ return indent(paint('dim', `${row.label}${' '.repeat(labelWidth - visibleWidth(row.label))} `)
76
73
  + paint(tone, `${horizontalBar(row.remaining, barWidth)} ${value}`)
77
74
  + paint('dim', ` ↺ ${row.reset}${row.amounts
78
75
  ? ` · ${money(row.amounts.remaining, row.amounts.currency)}/${money(row.amounts.limit, row.amounts.currency)}`
79
- : ''}`);
76
+ : ''}`));
80
77
  })];
81
78
  if (state.snapshot.allowance) {
82
79
  if (rows.length) lines.push('');
83
- lines.push(...allowanceLines(state.snapshot.allowance, paint, barWidth));
80
+ lines.push(...allowanceLines(state.snapshot.allowance, paint, barWidth).map(indent));
84
81
  }
85
82
  if (state.snapshot.balance) {
86
83
  if (rows.length || state.snapshot.allowance) lines.push('');
87
- lines.push(...balanceLines(state.snapshot.balance, paint));
84
+ lines.push(...balanceLines(state.snapshot.balance, paint).map(indent));
88
85
  }
89
- if (state.snapshot.spend) {
86
+ if (state.snapshot.spend?.today !== undefined) {
90
87
  if (rows.length || state.snapshot.allowance || state.snapshot.balance) lines.push('');
91
- lines.push(...spendLines(state.snapshot.spend, paint));
88
+ lines.push(...spendLines(state.snapshot.spend, paint).map(indent));
92
89
  }
93
90
  return lines;
94
91
  }
package/src/config.ts CHANGED
@@ -77,3 +77,6 @@ export function loadQuotaAdaptersFromSettings(settings: unknown): QuotaAdapter[]
77
77
  }
78
78
  });
79
79
  }
80
+
81
+ // Explicit adapter bindings from quotaUsage already have adapters registered; listing every
82
+ // registered adapter is the boundary between quota providers and ordinary model providers.
package/src/extension.ts CHANGED
@@ -3,6 +3,7 @@ import { renderFooter } from './bar/quota.ts';
3
3
  import { captureAllUsage, USAGE_ENTRY, usageCardComponent, usageLines } from './bar/card.ts';
4
4
  import type { UsageCard } from './bar/card.ts';
5
5
  import { codexAdapter } from './query/codex.ts';
6
+ import { createDeepSeekAdapter } from './query/deepseek.ts';
6
7
  import { kimiAdapter } from './query/kimi.ts';
7
8
  import { openCodeGoAdapter } from './query/opencode-go.ts';
8
9
  import { QuotaController } from './query/controller.ts';
@@ -16,6 +17,7 @@ const USAGE_WIDGET = 'quota-bar:usage';
16
17
 
17
18
  export interface QuotaExtensionOptions {
18
19
  adapters?: readonly QuotaAdapter[]; // Extra adapters; duplicate IDs are rejected.
20
+ providers?: readonly string[]; // Extra configured quota providers to include in /usage
19
21
  footer?: boolean; // false: query + /usage + structured events only
20
22
  intervalMs?: number;
21
23
  timeoutMs?: number;
@@ -23,7 +25,8 @@ export interface QuotaExtensionOptions {
23
25
 
24
26
  export function createQuotaExtension(options: QuotaExtensionOptions = {}) {
25
27
  return (pi: ExtensionAPI): void => {
26
- const registry = new AdapterRegistry([codexAdapter, kimiAdapter, openCodeGoAdapter, ...(options.adapters ?? [])]);
28
+ const registry = new AdapterRegistry([codexAdapter, kimiAdapter, openCodeGoAdapter,
29
+ createDeepSeekAdapter('deepseek'), ...(options.adapters ?? [])]);
27
30
  let current: ExtensionContext | undefined;
28
31
  let selectedProvider: string | undefined;
29
32
  let timer: ReturnType<typeof setInterval> | undefined;
@@ -97,7 +100,8 @@ export function createQuotaExtension(options: QuotaExtensionOptions = {}) {
97
100
  }
98
101
  };
99
102
  add(ctx.model?.provider);
100
- for (const model of ctx.modelRegistry.getAll()) add(model.provider);
103
+ for (const adapter of registry.list()) add(adapter.provider);
104
+ for (const provider of options.providers ?? []) add(provider);
101
105
  return providers;
102
106
  };
103
107