Unified · Split
| @@ -1,5 +1,10 @@ | ||
| 1 | 1 | import * as NodeServices from "@effect/platform-node/NodeServices"; |
| 2 | −import { ProviderDriverKind, ProviderInstanceId, type UsageDay } from "@t3tools/contracts"; | |
| 2 | +import { | |
| 3 | + ProviderDriverKind, | |
| 4 | + ProviderInstanceEnvironmentVariableName, | |
| 5 | + ProviderInstanceId, | |
| 6 | + type UsageDay, | |
| 7 | +} from "@t3tools/contracts"; | |
| 3 | 8 | import { expect, it } from "@effect/vitest"; |
| 4 | 9 | import * as Effect from "effect/Effect"; |
| 5 | 10 | import * as Layer from "effect/Layer"; |
| @@ -85,12 +90,26 @@ it.layer(NodeServices.layer)("UsageService", (it) => { · 79 unmodified lines | ||
| 85 | 90 | [ProviderInstanceId.make("codex-work")]: { |
| 86 | 91 | driver: ProviderDriverKind.make("codex"), |
| 87 | 92 | enabled: true, |
| 88 | − config: { homePath: "/virtual/codex-work" }, | |
| 93 | + config: {}, | |
| 94 | + environment: [ | |
| 95 | + { | |
| 96 | + name: ProviderInstanceEnvironmentVariableName.make("CODEX_HOME"), | |
| 97 | + value: "/virtual/codex-work", | |
| 98 | + sensitive: false, | |
| 99 | + }, | |
| 100 | + ], | |
| 89 | 101 | }, |
| 90 | 102 | [ProviderInstanceId.make("codex-work-copy")]: { |
| 91 | 103 | driver: ProviderDriverKind.make("codex"), |
| 92 | 104 | enabled: true, |
| 93 | − config: { homePath: "/virtual/codex-work" }, | |
| 105 | + config: {}, | |
| 106 | + environment: [ | |
| 107 | + { | |
| 108 | + name: ProviderInstanceEnvironmentVariableName.make("CODEX_HOME"), | |
| 109 | + value: "/virtual/codex-work", | |
| 110 | + sensitive: false, | |
| 111 | + }, | |
| 112 | + ], | |
| 94 | 113 | }, |
| 95 | 114 | }, |
| 96 | 115 | }), |
| @@ -41,6 +41,7 @@ import { ServerConfig } from "../config.ts"; · 40 unmodified lines | ||
| 41 | 41 | import * as ServerSettings from "../serverSettings.ts"; |
| 42 | 42 | import { resolveClaudeHomePath } from "../provider/Drivers/ClaudeHome.ts"; |
| 43 | 43 | import { resolveCodexHomeLayout } from "../provider/Drivers/CodexHomeLayout.ts"; |
| 44 | +import { mergeProviderInstanceEnvironment } from "../provider/ProviderInstanceEnvironment.ts"; | |
| 44 | 45 | import { UsageAggregator } from "./usageAggregation.ts"; |
| 45 | 46 | import { parseRateTable, type RateTable } from "./usagePricing.ts"; |
| 46 | 47 | import { |
| @@ -236,7 +237,10 @@ export const make = Effect.gen(function* () { · 189 unmodified lines | ||
| 236 | 237 | )) { |
| 237 | 238 | const config = decodeClaudeSettings(instance.config ?? {}); |
| 238 | 239 | if (Option.isNone(config)) continue; |
| 239 | − const claudeHome = yield* resolveClaudeHomePath(config.value); | |
| 240 | + const processEnv = mergeProviderInstanceEnvironment(instance.environment); | |
| 241 | + const claudeHome = yield* resolveClaudeHomePath({ | |
| 242 | + homePath: config.value.homePath || processEnv.CLAUDE_CONFIG_DIR || "", | |
| 243 | + }); | |
| 240 | 244 | addDir("claude", yield* resolveClaudeTranscriptDir(claudeHome)); |
| 241 | 245 | } |
| 242 | 246 | |
| @@ -246,7 +250,11 @@ export const make = Effect.gen(function* () { · 3 unmodified lines | ||
| 246 | 250 | )) { |
| 247 | 251 | const config = decodeCodexSettings(instance.config ?? {}); |
| 248 | 252 | if (Option.isNone(config)) continue; |
| 249 | − const codexLayout = yield* resolveCodexHomeLayout(config.value); | |
| 253 | + const processEnv = mergeProviderInstanceEnvironment(instance.environment); | |
| 254 | + const codexLayout = yield* resolveCodexHomeLayout({ | |
| 255 | + ...config.value, | |
| 256 | + homePath: config.value.homePath || processEnv.CODEX_HOME || "", | |
| 257 | + }); | |
| 250 | 258 | addDir("codex", path.join(codexLayout.sharedHomePath, "sessions")); |
| 251 | 259 | } |
| 252 | 260 | return dirs; |
| @@ -62,9 +62,6 @@ 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; | |
| 68 | 65 | |
| 69 | 66 | // Hold the content until every environment is terminal. Rendering merged |
| 70 | 67 | // totals while devices are still answering makes every number on the page |
| @@ -217,7 +214,7 @@ export function UsagePage() { · 146 unmodified lines | ||
| 217 | 214 | {settling ? ( |
| 218 | 215 | <> |
| 219 | 216 | {environments.length > 1 ? <UsageDeviceStrip environments={environments} /> : null} |
| 220 | − <UsageSkeleton providers={skeletonProviders} /> | |
| 217 | + <UsageSkeleton /> | |
| 221 | 218 | </> |
| 222 | 219 | ) : ( |
| 223 | 220 | <> |
| @@ -591,7 +588,7 @@ function UsageDeviceStrip({ · 367 unmodified lines | ||
| 591 | 588 | * Static stand-in with the loaded page's shape. No shimmer; blocks fill in |
| 592 | 589 | * exactly once when the last device answers. |
| 593 | 590 | */ |
| 594 | −function UsageSkeleton({ providers }: { readonly providers: readonly UsageProviderKind[] }) { | |
| 591 | +function UsageSkeleton() { | |
| 595 | 592 | return ( |
| 596 | 593 | <> |
| 597 | 594 | <section className="grid gap-6 lg:grid-cols-[minmax(0,16rem)_minmax(0,1fr)]"> |
| @@ -600,7 +597,7 @@ function UsageSkeleton({ providers }: { readonly providers: readonly UsageProvid · 2 unmodified lines | ||
| 600 | 597 | <div className="h-10 w-36 rounded-sm bg-muted" /> |
| 601 | 598 | <div className="h-4 w-32 rounded-sm bg-muted" /> |
| 602 | 599 | </div> |
| 603 | − {providers.map((provider) => ( | |
| 600 | + {PROVIDER_ORDER.map((provider) => ( | |
| 604 | 601 | <div key={provider} className="flex flex-col gap-1"> |
| 605 | 602 | <div className="flex min-h-5 items-center justify-between gap-4"> |
| 606 | 603 | <span className="flex items-center gap-2"> |