authorgravatar for julius0216@outlook.comJulius Marminge <julius0216@outlook.com> 2026-09-06 00:07:04-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-09-06 00:07:04-07:00
log00d6109cbb1a13712d7347701969870aee83252d
tree756533bec226708bf9aa462bc18b1192f40724b4
parentb273d1cfe9515f905d37a9146bb275b905d14399
signature Signed by PGP key B5690EEEBB952194

chore(web): remove usage limits demo fixtures (#10330)


3 files changed, 6 insertions(+), 454 deletions(-)

apps/web/src/components/usage/UsageLimits.tsx+1-7
...@@ -9,7 +9,6 @@ import {...@@ -9,7 +9,6 @@ import {
9} from "@t3tools/contracts";9} from "@t3tools/contracts";
10import { useAtomValue } from "@effect/atom-react";10import { useAtomValue } from "@effect/atom-react";
11import {11import {
12 collectLimitAccounts,
13 elapsedShare,12 elapsedShare,
14 formatDuration,13 formatDuration,
15 formatResetsIn,14 formatResetsIn,
...@@ -36,7 +35,6 @@ import {...@@ -36,7 +35,6 @@ import {
36} from "../ui/alert-dialog";35} from "../ui/alert-dialog";
37import { Button } from "../ui/button";36import { Button } from "../ui/button";
38import { Tooltip, TooltipPopup, TooltipTrigger } from "../ui/tooltip";37import { Tooltip, TooltipPopup, TooltipTrigger } from "../ui/tooltip";
39import type { makeLimitsFixture } from "./usageLimitsFixture";
40import { UsageLimitsPooled } from "./UsageLimitsPooled";38import { UsageLimitsPooled } from "./UsageLimitsPooled";
41import { PROVIDER_PRESENTATION } from "./usageProviders";39import { PROVIDER_PRESENTATION } from "./usageProviders";
4240
...@@ -323,16 +321,12 @@ export function ResetCredits({...@@ -323,16 +321,12 @@ export function ResetCredits({
323 */321 */
324export function UsageLimitsSection({322export function UsageLimitsSection({
325 selectedEnvironmentIds,323 selectedEnvironmentIds,
326 fixture = null,
327}: {324}: {
328 readonly selectedEnvironmentIds: ReadonlySet<EnvironmentId> | null;325 readonly selectedEnvironmentIds: ReadonlySet<EnvironmentId> | null;
329 /** Dev-only synthetic presentations standing in for the live ones. */
330 readonly fixture?: ReturnType<typeof makeLimitsFixture> | null;
331}) {326}) {
332 const live = useAtomValue(environmentPresentations.presentationsAtom);327 const presentations = useAtomValue(environmentPresentations.presentationsAtom);
333 // Anchored once per mount on purpose: countdowns must not tick (see above).328 // Anchored once per mount on purpose: countdowns must not tick (see above).
334 const [now] = useState(() => Date.now());329 const [now] = useState(() => Date.now());
335 const presentations: Parameters<typeof collectLimitAccounts>[0] = fixture ?? live;
336 const selected =330 const selected =
337 selectedEnvironmentIds === null331 selectedEnvironmentIds === null
338 ? presentations332 ? presentations
apps/web/src/components/usage/UsagePage.tsx+5-34
...@@ -59,7 +59,6 @@ import {...@@ -59,7 +59,6 @@ import {
59import { WorkspacePageContainer } from "../WorkspacePageContainer";59import { WorkspacePageContainer } from "../WorkspacePageContainer";
60import { WorkspacePageHeader } from "../WorkspacePageHeader";60import { WorkspacePageHeader } from "../WorkspacePageHeader";
61import { UsageLimitsSection } from "./UsageLimits";61import { UsageLimitsSection } from "./UsageLimits";
62import { makeLimitsFixture } from "./usageLimitsFixture";
63import { UsagePriceOverrides } from "./UsagePriceOverrides";62import { UsagePriceOverrides } from "./UsagePriceOverrides";
64import { UsageProviderChart, type UsageChartMetric } from "./UsageProviderChart";63import { UsageProviderChart, type UsageChartMetric } from "./UsageProviderChart";
65import { PROVIDER_ORDER, PROVIDER_PRESENTATION, providersWithUsage } from "./usageProviders";64import { PROVIDER_ORDER, PROVIDER_PRESENTATION, providersWithUsage } from "./usageProviders";
...@@ -110,33 +109,10 @@ export function UsagePage() {...@@ -110,33 +109,10 @@ export function UsagePage() {
110 useState<ReadonlySet<EnvironmentId> | null>(null);109 useState<ReadonlySet<EnvironmentId> | null>(null);
111 const { days: windowDays, window } = windowSelection;110 const { days: windowDays, window } = windowSelection;
112 const isPast24Hours = windowDays === 1;111 const isPast24Hours = windowDays === 1;
113 const usage = useUsage(window, selectedEnvironmentIds);112 const { merged, environments, selectedEnvironments, isPending, isPartial, refresh } = useUsage(
114 // Dev only: `/usage?limitsFixture=<name>` lists synthetic environments in the113 window,
115 // picker and feeds the Limits view from them, so merge rules can be eyeballed.114 selectedEnvironmentIds,
116 const [fixture] = useState(() => {115 );
117 if (!import.meta.env.DEV) return null;
118 const name = new URLSearchParams(globalThis.location.search).get("limitsFixture");
119 return name ? makeLimitsFixture(name, Date.now()) : null;
120 });
121 const { merged, environments, selectedEnvironments, isPending, isPartial, refresh } =
122 useMemo(() => {
123 if (!fixture || !showingLimits) return usage;
124 const all = [...fixture].map(([environmentId, presentation]) => ({
125 environmentId,
126 label: presentation.entry.target.label,
127 isPending: false,
128 error: null,
129 summary: null,
130 }));
131 return {
132 ...usage,
133 environments: all,
134 selectedEnvironments:
135 selectedEnvironmentIds === null
136 ? all
137 : all.filter((environment) => selectedEnvironmentIds.has(environment.environmentId)),
138 };
139 }, [fixture, selectedEnvironmentIds, showingLimits, usage]);
140 const presentations = useAtomValue(environmentPresentations.presentationsAtom);116 const presentations = useAtomValue(environmentPresentations.presentationsAtom);
141 const refreshProviders = useAtomCommand(serverEnvironment.refreshProviders, {117 const refreshProviders = useAtomCommand(serverEnvironment.refreshProviders, {
142 reportFailure: false,118 reportFailure: false,
...@@ -190,8 +166,6 @@ export function UsagePage() {...@@ -190,8 +166,6 @@ export function UsagePage() {
190 if (refreshingRef.current) return;166 if (refreshingRef.current) return;
191167
192 if (showingLimits) {168 if (showingLimits) {
193 // Synthetic data has nothing to re-read.
194 if (fixture) return;
195 refreshingRef.current = true;169 refreshingRef.current = true;
196 setIsRefreshing(true);170 setIsRefreshing(true);
197 void Promise.all(171 void Promise.all(
...@@ -375,10 +349,7 @@ export function UsagePage() {...@@ -375,10 +349,7 @@ export function UsagePage() {
375 : `Select an environment to see ${showingLimits ? "limits" : "usage"}.`}349 : `Select an environment to see ${showingLimits ? "limits" : "usage"}.`}
376 </p>350 </p>
377 ) : showingLimits ? (351 ) : showingLimits ? (
378 <UsageLimitsSection352 <UsageLimitsSection selectedEnvironmentIds={selectedEnvironmentIds} />
379 selectedEnvironmentIds={selectedEnvironmentIds}
380 fixture={fixture}
381 />
382 ) : isPending ? (353 ) : isPending ? (
383 <UsageSkeleton />354 <UsageSkeleton />
384 ) : (355 ) : (
apps/web/src/components/usage/usageLimitsFixture.ts deleted-413
...@@ -1,413 +0,0 @@
1/**
2 * Dev-only stand-ins for `environmentPresentations` that exercise the pooled
3 * Limits view's merge rules, one scenario per named fixture. Reached with
4 * `/usage?limitsFixture=<name>` on a dev build; never bundled otherwise.
5 */
6import {
7 EnvironmentId,
8 ProviderDriverKind,
9 ProviderInstanceId,
10 type ServerProvider,
11 type ServerProviderUsageWindow,
12 type UsageLimitSourceSnapshot,
13 UsageLimitSourceId,
14} from "@t3tools/contracts";
15
16const MINUTE = 60_000;
17const HOUR = 60 * MINUTE;
18const DAY = 24 * HOUR;
19
20interface Presentation {
21 readonly entry: { readonly target: { readonly label: string } };
22 readonly serverConfig: {
23 readonly providers: readonly ServerProvider[];
24 readonly usageLimitSources: readonly UsageLimitSourceSnapshot[];
25 };
26}
27
28type Fixture = ReadonlyMap<EnvironmentId, Presentation>;
29
30const codex = ProviderDriverKind.make("codex");
31const claude = ProviderDriverKind.make("claudeAgent");
32
33function makeHelpers(now: number) {
34 const at = (ms: number) => new Date(now + ms).toISOString();
35 const checked = (agoMs: number) => new Date(now - agoMs).toISOString();
36
37 const session = (used: number, resetsInMs: number): ServerProviderUsageWindow => ({
38 id: "five_hour",
39 kind: "session",
40 label: "Session",
41 usedPercent: used,
42 windowDurationMins: 300,
43 resetsAt: at(resetsInMs),
44 });
45 const weekly = (
46 id: string,
47 label: string,
48 used: number,
49 resetsInMs: number,
50 ): ServerProviderUsageWindow => ({
51 id,
52 kind: "weekly",
53 label,
54 usedPercent: used,
55 windowDurationMins: 7 * 24 * 60,
56 resetsAt: at(resetsInMs),
57 });
58 /** Codex names its five-hour window `primary` and its weekly one `secondary`. */
59 const codexSession = (used: number, resetsInMs: number): ServerProviderUsageWindow => ({
60 ...session(used, resetsInMs),
61 id: "primary",
62 });
63 const codexWeekly = (used: number, resetsInMs: number) =>
64 weekly("secondary", "Weekly", used, resetsInMs);
65
66 const provider = (
67 overrides: Partial<ServerProvider> & Pick<ServerProvider, "instanceId" | "driver">,
68 ): ServerProvider => ({
69 enabled: true,
70 installed: true,
71 version: null,
72 status: "ready",
73 auth: { status: "authenticated" },
74 checkedAt: checked(0),
75 models: [],
76 slashCommands: [],
77 skills: [],
78 ...overrides,
79 });
80
81 const codexInstance = (input: {
82 readonly instanceId: string;
83 readonly displayName?: string;
84 readonly accentColor?: string;
85 readonly email: string;
86 readonly plan?: string;
87 readonly checkedAgoMs?: number;
88 readonly windows: readonly ServerProviderUsageWindow[];
89 readonly credits?: number;
90 }) =>
91 provider({
92 instanceId: ProviderInstanceId.make(input.instanceId),
93 driver: codex,
94 ...(input.displayName ? { displayName: input.displayName } : {}),
95 ...(input.accentColor ? { accentColor: input.accentColor } : {}),
96 auth: {
97 status: "authenticated",
98 label: input.plan ?? "ChatGPT Pro 20x Subscription",
99 email: input.email,
100 },
101 usageLimits: {
102 checkedAt: checked(input.checkedAgoMs ?? MINUTE),
103 windows: input.windows,
104 ...(input.credits
105 ? { resetCredits: { availableCount: input.credits, nextExpiresAt: at(28 * DAY) } }
106 : {}),
107 },
108 });
109
110 const claudeHubAccount = (
111 email: string | null,
112 windows: readonly ServerProviderUsageWindow[],
113 checkedAgoMs = 4 * MINUTE,
114 ): UsageLimitSourceSnapshot["accounts"][number] => ({
115 id: email ? `claude-${email}.json` : "claude-team-seat.json",
116 driver: claude,
117 ...(email ? { email } : {}),
118 plan: "Claude Subscription",
119 usageLimits: { checkedAt: checked(checkedAgoMs), windows },
120 });
121
122 const hub = (
123 id: string,
124 label: string,
125 accounts: UsageLimitSourceSnapshot["accounts"],
126 error?: string,
127 ): UsageLimitSourceSnapshot => ({
128 id: UsageLimitSourceId.make(id),
129 kind: "cliproxy",
130 label,
131 checkedAt: checked(2 * MINUTE),
132 accounts,
133 ...(error ? { error } : {}),
134 });
135
136 const environment = (
137 id: string,
138 label: string,
139 providers: readonly ServerProvider[],
140 usageLimitSources: readonly UsageLimitSourceSnapshot[] = [],
141 ): readonly [EnvironmentId, Presentation] => [
142 EnvironmentId.make(id),
143 { entry: { target: { label } }, serverConfig: { providers, usageLimitSources } },
144 ];
145
146 return {
147 at,
148 checked,
149 session,
150 weekly,
151 codexSession,
152 codexWeekly,
153 provider,
154 codexInstance,
155 claudeHubAccount,
156 hub,
157 environment,
158 };
159}
160
161const FIXTURES: Record<string, (now: number) => Fixture> = {
162 /**
163 * The same Codex account signed in on two machines with different snapshot
164 * ages, plus a hub that also reports it. Must collapse to one segment with
165 * the freshest figures and both machines listed.
166 */
167 "same-account": (now) => {
168 const h = makeHelpers(now);
169 const email = "main@example.com";
170 const hubAccounts: UsageLimitSourceSnapshot["accounts"] = [
171 {
172 id: `codex-abc-${email}-pro.json`,
173 driver: codex,
174 email,
175 plan: "ChatGPT Pro 20x Subscription",
176 usageLimits: { checkedAt: h.checked(14 * MINUTE), windows: [h.codexWeekly(70, 5 * DAY)] },
177 },
178 ];
179 return new Map([
180 h.environment(
181 "env-macbook",
182 "MacBook Pro",
183 [
184 h.codexInstance({
185 instanceId: "codex",
186 displayName: "Codex Personal",
187 accentColor: "#6366f1",
188 email,
189 windows: [h.codexSession(10, 3 * HOUR), h.codexWeekly(66, 5 * DAY)],
190 credits: 2,
191 }),
192 ],
193 [h.hub("cliproxy-nucbox", "CLI Proxy", hubAccounts)],
194 ),
195 h.environment("env-nucbox", "nucbox-1", [
196 h.codexInstance({
197 instanceId: "codex",
198 email,
199 checkedAgoMs: 9 * MINUTE,
200 windows: [h.codexSession(30, 3 * HOUR), h.codexWeekly(60, 5 * DAY)],
201 credits: 2,
202 }),
203 ]),
204 ]);
205 },
206
207 /**
208 * Three machines, no two alike: one has only Codex, one only Claude via a
209 * hub, one has both natively. Filtering to any single environment should
210 * drop whole provider sections.
211 */
212 "uneven-environments": (now) => {
213 const h = makeHelpers(now);
214 return new Map([
215 h.environment("env-macbook", "MacBook Pro", [
216 h.codexInstance({
217 instanceId: "codex",
218 displayName: "Codex Personal",
219 accentColor: "#6366f1",
220 email: "main@example.com",
221 windows: [h.codexSession(10, 3 * HOUR), h.codexWeekly(66, 5 * DAY)],
222 credits: 2,
223 }),
224 h.provider({
225 instanceId: ProviderInstanceId.make("claude"),
226 driver: claude,
227 auth: { status: "authenticated", label: "Claude Max", email: "main@example.com" },
228 usageLimits: {
229 checkedAt: h.checked(MINUTE),
230 windows: [
231 h.session(2, 4 * HOUR),
232 h.weekly("seven_day", "Weekly", 38, 4 * DAY),
233 h.weekly("seven_day_fable", "Weekly · Fable", 69, 4 * DAY),
234 ],
235 },
236 }),
237 ]),
238 h.environment(
239 "env-nucbox",
240 "nucbox-1",
241 [],
242 [
243 h.hub("cliproxy-nucbox", "CLI Proxy", [
244 h.claudeHubAccount("personal@example.com", [
245 h.session(100, 4 * HOUR),
246 h.weekly("seven_day", "Weekly", 50, DAY),
247 h.weekly("seven_day_fable", "Weekly · Fable", 96, DAY),
248 ]),
249 h.claudeHubAccount("second@example.org", [
250 h.session(63, 2 * HOUR),
251 h.weekly("seven_day", "Weekly", 37, 4 * DAY),
252 h.weekly("seven_day_fable", "Weekly · Fable", 72, 4 * DAY),
253 ]),
254 ]),
255 ],
256 ),
257 h.environment("env-macmini", "Mac Mini", [
258 h.codexInstance({
259 instanceId: "codex",
260 displayName: "Codex Work",
261 email: "work@example.com",
262 windows: [h.codexSession(0, 5 * HOUR), h.codexWeekly(95, 5 * DAY)],
263 credits: 1,
264 }),
265 ]),
266 ]);
267 },
268
269 /**
270 * Codex plans that report only one window (Go reports a monthly allowance;
271 * a hub often has no five-hour figure for an account) mixed with a plan that
272 * reports both. Each pool lists only the accounts that have that window.
273 */
274 "codex-window-mix": (now) => {
275 const h = makeHelpers(now);
276 return new Map([
277 h.environment(
278 "env-macbook",
279 "MacBook Pro",
280 [
281 h.codexInstance({
282 instanceId: "codex",
283 displayName: "Codex Personal",
284 accentColor: "#6366f1",
285 email: "main@example.com",
286 windows: [h.codexSession(40, 2 * HOUR), h.codexWeekly(55, 3 * DAY)],
287 credits: 2,
288 }),
289 h.codexInstance({
290 instanceId: "codex-go",
291 displayName: "Codex Go",
292 accentColor: "#10b981",
293 email: "go@example.com",
294 plan: "ChatGPT Go Subscription",
295 windows: [
296 {
297 id: "primary",
298 kind: "monthly",
299 label: "Monthly",
300 usedPercent: 82,
301 windowDurationMins: 30 * 24 * 60,
302 resetsAt: h.at(11 * DAY),
303 },
304 ],
305 }),
306 ],
307 [
308 h.hub("cliproxy-nucbox", "CLI Proxy", [
309 {
310 id: "codex-def-work@example.com-pro.json",
311 driver: codex,
312 email: "work@example.com",
313 plan: "ChatGPT Pro 20x Subscription",
314 usageLimits: {
315 checkedAt: h.checked(3 * MINUTE),
316 windows: [h.codexWeekly(88, 6 * DAY)],
317 },
318 },
319 {
320 id: "codex-ghi-team@example.net-plus.json",
321 driver: codex,
322 email: "team@example.net",
323 plan: "ChatGPT Plus Subscription",
324 usageLimits: {
325 checkedAt: h.checked(3 * MINUTE),
326 windows: [h.codexWeekly(12, DAY)],
327 },
328 },
329 ]),
330 ],
331 ),
332 ]);
333 },
334
335 /**
336 * A hub configured on two environments, a hub that is down, a provider
337 * whose probe failed, an API-key account, and a hub account with no email.
338 */
339 "failures-and-strays": (now) => {
340 const h = makeHelpers(now);
341 const hubAccounts: UsageLimitSourceSnapshot["accounts"] = [
342 h.claudeHubAccount("main@example.com", [
343 h.session(2, 4 * HOUR),
344 h.weekly("seven_day", "Weekly", 38, 4 * DAY),
345 h.weekly("seven_day_fable", "Weekly · Fable", 69, 4 * DAY),
346 ]),
347 h.claudeHubAccount(null, [
348 h.session(40, 2 * HOUR),
349 h.weekly("seven_day", "Weekly", 20, 6 * DAY),
350 ]),
351 ];
352 return new Map([
353 h.environment(
354 "env-macbook",
355 "MacBook Pro",
356 [
357 h.provider({
358 instanceId: ProviderInstanceId.make("claude"),
359 driver: claude,
360 auth: { status: "authenticated", label: "Claude API Key" },
361 usageLimits: {
362 checkedAt: h.checked(MINUTE),
363 windows: [],
364 unavailable: {
365 reason: "unsupported",
366 message: "This account has no subscription limits.",
367 },
368 },
369 }),
370 ],
371 [h.hub("cliproxy-nucbox", "CLI Proxy", hubAccounts)],
372 ),
373 h.environment(
374 "env-nucbox",
375 "nucbox-1",
376 [],
377 [h.hub("cliproxy-nucbox", "CLI Proxy", hubAccounts)],
378 ),
379 h.environment(
380 "env-macmini",
381 "Mac Mini",
382 [
383 h.provider({
384 instanceId: ProviderInstanceId.make("claude"),
385 driver: claude,
386 auth: {
387 status: "authenticated",
388 label: "Claude Max",
389 email: "work@example.com",
390 },
391 usageLimits: {
392 checkedAt: h.checked(MINUTE),
393 windows: [],
394 unavailable: { reason: "probeFailed", message: "Claude timed out reading usage." },
395 },
396 }),
397 ],
398 [
399 h.hub(
400 "cliproxy-aws",
401 "AWS proxy",
402 [],
403 "fetch failed: connect ECONNREFUSED 10.0.0.4:8318",
404 ),
405 ],
406 ),
407 ]);
408 },
409};
410
411export function makeLimitsFixture(name: string, now: number): Fixture | null {
412 return Object.hasOwn(FIXTURES, name) ? FIXTURES[name]!(now) : null;
413}