Unified · Split
| @@ -105,4 +105,45 @@ it.layer(NodeServices.layer)("UsageService", (it) => { · 104 unmodified lines | ||
| 105 | 105 | ), |
| 106 | 106 | ), |
| 107 | 107 | ); |
| 108 | + | |
| 109 | + it.effect("uses driver defaults when an enabled instance omits config", () => | |
| 110 | + Effect.gen(function* () { | |
| 111 | + const usage = yield* UsageService.UsageService; | |
| 112 | + const summary = yield* usage.readSummary(summaryInput); | |
| 113 | + | |
| 114 | + expect(summary.sources.map((source) => source.fingerprint.provider)).toEqual(["codex"]); | |
| 115 | + }).pipe( | |
| 116 | + Effect.provide( | |
| 117 | + UsageService.layer.pipe( | |
| 118 | + Layer.provideMerge( | |
| 119 | + Layer.mergeAll( | |
| 120 | + ServerSettings.ServerSettingsService.layerTest({ | |
| 121 | + providerInstances: { | |
| 122 | + [ProviderInstanceId.make("claudeAgent")]: { | |
| 123 | + driver: ProviderDriverKind.make("claudeAgent"), | |
| 124 | + enabled: false, | |
| 125 | + config: {}, | |
| 126 | + }, | |
| 127 | + [ProviderInstanceId.make("codex")]: { | |
| 128 | + driver: ProviderDriverKind.make("codex"), | |
| 129 | + enabled: false, | |
| 130 | + config: {}, | |
| 131 | + }, | |
| 132 | + [ProviderInstanceId.make("codex-work")]: { | |
| 133 | + driver: ProviderDriverKind.make("codex"), | |
| 134 | + enabled: true, | |
| 135 | + }, | |
| 136 | + }, | |
| 137 | + }), | |
| 138 | + ServerConfig.layerTest(process.cwd(), { prefix: "t3-usage-test-" }), | |
| 139 | + Layer.succeed( | |
| 140 | + HttpClient.HttpClient, | |
| 141 | + HttpClient.make(() => Effect.die("unexpected usage pricing request")), | |
| 142 | + ), | |
| 143 | + ), | |
| 144 | + ), | |
| 145 | + ), | |
| 146 | + ), | |
| 147 | + ), | |
| 148 | + ); | |
| 108 | 149 | }); |
| @@ -234,7 +234,7 @@ export const make = Effect.gen(function* () { · 233 unmodified lines | ||
| 234 | 234 | settings, |
| 235 | 235 | ProviderDriverKind.make("claudeAgent"), |
| 236 | 236 | )) { |
| 237 | − const config = decodeClaudeSettings(instance.config); | |
| 237 | + const config = decodeClaudeSettings(instance.config ?? {}); | |
| 238 | 238 | if (Option.isNone(config)) continue; |
| 239 | 239 | const claudeHome = yield* resolveClaudeHomePath(config.value); |
| 240 | 240 | addDir("claude", yield* resolveClaudeTranscriptDir(claudeHome)); |
| @@ -244,7 +244,7 @@ export const make = Effect.gen(function* () { · 3 unmodified lines | ||
| 244 | 244 | settings, |
| 245 | 245 | ProviderDriverKind.make("codex"), |
| 246 | 246 | )) { |
| 247 | − const config = decodeCodexSettings(instance.config); | |
| 247 | + const config = decodeCodexSettings(instance.config ?? {}); | |
| 248 | 248 | if (Option.isNone(config)) continue; |
| 249 | 249 | const codexLayout = yield* resolveCodexHomeLayout(config.value); |
| 250 | 250 | addDir("codex", path.join(codexLayout.sharedHomePath, "sessions")); |
| @@ -62,6 +62,9 @@ export function UsagePage() { · 61 unmodified lines | ||
| 62 | 62 | } |
| 63 | 63 | return PROVIDER_ORDER.filter((provider) => enabled.has(provider)); |
| 64 | 64 | }, [environments, merged.staleEnvironments]); |
| 65 | + const skeletonProviders = environments.some((environment) => environment.summary !== null) | |
| 66 | + ? providers | |
| 67 | + : PROVIDER_ORDER; | |
| 65 | 68 | |
| 66 | 69 | // Hold the content until every environment is terminal. Rendering merged |
| 67 | 70 | // totals while devices are still answering makes every number on the page |
| @@ -214,7 +217,7 @@ export function UsagePage() { · 146 unmodified lines | ||
| 214 | 217 | {settling ? ( |
| 215 | 218 | <> |
| 216 | 219 | {environments.length > 1 ? <UsageDeviceStrip environments={environments} /> : null} |
| 217 | − <UsageSkeleton /> | |
| 220 | + <UsageSkeleton providers={skeletonProviders} /> | |
| 218 | 221 | </> |
| 219 | 222 | ) : ( |
| 220 | 223 | <> |
| @@ -588,7 +591,7 @@ function UsageDeviceStrip({ · 367 unmodified lines | ||
| 588 | 591 | * Static stand-in with the loaded page's shape. No shimmer; blocks fill in |
| 589 | 592 | * exactly once when the last device answers. |
| 590 | 593 | */ |
| 591 | −function UsageSkeleton() { | |
| 594 | +function UsageSkeleton({ providers }: { readonly providers: readonly UsageProviderKind[] }) { | |
| 592 | 595 | return ( |
| 593 | 596 | <> |
| 594 | 597 | <section className="grid gap-6 lg:grid-cols-[minmax(0,16rem)_minmax(0,1fr)]"> |
| @@ -597,7 +600,7 @@ function UsageSkeleton() { · 2 unmodified lines | ||
| 597 | 600 | <div className="h-10 w-36 rounded-sm bg-muted" /> |
| 598 | 601 | <div className="h-4 w-32 rounded-sm bg-muted" /> |
| 599 | 602 | </div> |
| 600 | − {PROVIDER_ORDER.map((provider) => ( | |
| 603 | + {providers.map((provider) => ( | |
| 601 | 604 | <div key={provider} className="flex flex-col gap-1"> |
| 602 | 605 | <div className="flex min-h-5 items-center justify-between gap-4"> |
| 603 | 606 | <span className="flex items-center gap-2"> |