Open History to see the last commit
1import {
2 collectLimitAccounts,
3 collectLimitPools,
4 type LimitAccount,
5 type LimitPoolWindow,
6 type LimitPresentations,
7} from "@t3tools/shared/usageLimits";
8
9import type { WidgetTheme } from "./widgetTheme";
10
11export interface SubscriptionUsageSnapshot {
12 theme?: WidgetTheme;
13 url?: string;
14 checkedAt: number;
15 providers: Array<{
16 name: string;
17 detail: string;
18 windows: Array<{ kind?: string; label: string; remaining: number; reset: string }>;
19 expiresAt: number;
20 totalWindows: number;
21 }>;
22}
23
24// Snapshots expire after 15 minutes; suspended refresh requires a separate authenticated transport.
25const SNAPSHOT_MAX_AGE = 15 * 60_000;
26export const WIDGET_REFRESH_INTERVAL = 5 * 60_000;
27
28export function createWidgetRefresher<Id>(refresh: (id: Id) => Promise<unknown>) {
29 const attempted = new Map<Id, number>();
30 const pending = new Set<Id>();
31 return async (connected: readonly Id[], now: number) => {
32 await Promise.allSettled(
33 connected.map(async (id) => {
34 if (pending.has(id) || now - (attempted.get(id) ?? -Infinity) < WIDGET_REFRESH_INTERVAL)
35 return;
36 attempted.set(id, now);
37 pending.add(id);
38 try {
39 await refresh(id);
40 } finally {
41 pending.delete(id);
42 }
43 }),
44 );
45 };
46}
47
48// A reset at or before its account's observation already happened, so it cannot expire the snapshot.
49const isUpcoming = (reset: LimitPoolWindow["resets"][number]) =>
50 reset.at > Date.parse(reset.member.account.limits.checkedAt);
51
52function subscriptionUsageProps(
53 accounts: readonly LimitAccount[],
54 now: number,
55 configuredDrivers: ReadonlySet<string>,
56 maxWindowsPerProvider: number,
57): SubscriptionUsageSnapshot {
58 const pools = collectLimitPools(accounts, now);
59 const checked = accounts
60 .filter((account) => account.driver === "codex" || account.driver === "claudeAgent")
61 .map((account) => Date.parse(account.limits.checkedAt));
62 return {
63 checkedAt: checked.length > 0 && checked.every(Number.isFinite) ? Math.min(...checked) : 0,
64 providers: (["codex", "claudeAgent"] as const)
65 .filter(
66 (driver) =>
67 configuredDrivers.has(driver) || accounts.some((account) => account.driver === driver),
68 )
69 .map((driver) => {
70 const pool = pools.find((candidate) => candidate.driver === driver);
71 const name = driver === "codex" ? "Codex" : "Claude";
72 if (!pool)
73 return {
74 name,
75 detail: "No limits available",
76 windows: [],
77 expiresAt: 0,
78 totalWindows: 0,
79 };
80 const checkedAt = Math.min(...pool.accounts.map((a) => Date.parse(a.limits.checkedAt)));
81 const expiresAt = Math.min(
82 checkedAt + SNAPSHOT_MAX_AGE,
83 ...pool.windows.flatMap((window) =>
84 window.resets.filter(isUpcoming).map((reset) => reset.at),
85 ),
86 );
87 const fresh = Number.isFinite(expiresAt) && expiresAt > now;
88 const sortedWindows = [...pool.windows].sort(
89 (a, b) => a.remainingPercent - b.remainingPercent,
90 );
91 const selectedWindows = [
92 ...new Set([
93 sortedWindows.find((window) => window.kind === "session"),
94 sortedWindows.find((window) => window.kind === "weekly"),
95 ...sortedWindows,
96 ]),
97 ]
98 .filter((window) => window !== undefined)
99 .slice(0, maxWindowsPerProvider)
100 .sort((a, b) => a.remainingPercent - b.remainingPercent);
101 return {
102 name,
103 detail: !fresh
104 ? "Open T3 to refresh"
105 : pool.accounts.length > 1
106 ? `${pool.accounts.length} accounts · pooled`
107 : "Subscription remaining",
108 expiresAt: fresh ? expiresAt : 0,
109 totalWindows: fresh ? pool.windows.length : 0,
110 windows: fresh
111 ? selectedWindows.map((window) => ({
112 kind: window.kind,
113 label: window.label,
114 remaining: Math.round(window.remainingPercent),
115 reset: window.resets[0]
116 ? `${isUpcoming(window.resets[0]) ? "Next reset" : "Reset"} ${new Date(
117 window.resets[0].at,
118 ).toLocaleString(undefined, {
119 month: "short",
120 day: "numeric",
121 hour: "numeric",
122 minute: "2-digit",
123 })}`
124 : "Reset time unavailable",
125 }))
126 : [],
127 };
128 }),
129 };
130}
131
132export function buildSubscriptionUsageSnapshot(
133 presentations: LimitPresentations,
134 url: string,
135 maxWindowsPerProvider = 6,
136): SubscriptionUsageSnapshot {
137 const configuredDrivers = new Set(
138 [...presentations.values()].flatMap((presentation) =>
139 (presentation.serverConfig?.providers ?? [])
140 // Servers report default-enabled drivers even when their CLI is missing.
141 .filter(
142 (provider) =>
143 provider.enabled &&
144 provider.installed &&
145 provider.usageLimits?.unavailable?.reason !== "unsupported",
146 )
147 .map((provider) => provider.driver),
148 ),
149 );
150 return {
151 ...subscriptionUsageProps(
152 collectLimitAccounts(presentations),
153 0,
154 configuredDrivers,
155 maxWindowsPerProvider,
156 ),
157 url,
158 };
159}
160
161export function subscriptionUsageTimeline(snapshot: SubscriptionUsageSnapshot, now: number) {
162 const deadlines = [...new Set(snapshot.providers.map((p) => p.expiresAt))]
163 .filter((deadline) => deadline > now)
164 .sort((a, b) => a - b);
165 return [now, ...deadlines].map((date) => ({
166 date: new Date(date),
167 props: {
168 ...snapshot,
169 providers: snapshot.providers.map((provider) =>
170 provider.windows.length > 0 && provider.expiresAt <= date
171 ? { ...provider, detail: "Open T3 to refresh", windows: [], totalWindows: 0 }
172 : provider,
173 ),
174 },
175 }));
176}