-
Notifications
You must be signed in to change notification settings - Fork 0
refactor: extract shared account view helpers (Architecture PR #1) #33
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
2013dbe
00dba6e
0d96ab3
a0b9d72
f8bf733
0e0edf8
6b7b2e8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,82 @@ | ||
| import { MODEL_FAMILIES, type ModelFamily } from "../prompts/codex.js"; | ||
| import { formatWaitTime } from "./rate-limits.js"; | ||
|
|
||
| export interface ActiveAccountStorageView { | ||
| activeIndex: number; | ||
| activeIndexByFamily?: Partial<Record<ModelFamily, number>>; | ||
| accounts: unknown[]; | ||
| } | ||
|
|
||
| export interface AccountRateLimitView { | ||
| rateLimitResetTimes?: Record<string, number | undefined>; | ||
| } | ||
|
|
||
| export type ActiveIndexByFamilyView = Partial<Record<ModelFamily, number>> | undefined; | ||
|
|
||
| export function resolveActiveIndex( | ||
| storage: ActiveAccountStorageView, | ||
| family: ModelFamily = "codex", | ||
| ): number { | ||
| const total = storage.accounts.length; | ||
| if (total === 0) return 0; | ||
| const rawCandidate = storage.activeIndexByFamily?.[family] ?? storage.activeIndex; | ||
| const raw = Number.isFinite(rawCandidate) ? rawCandidate : 0; | ||
| return Math.max(0, Math.min(raw, total - 1)); | ||
| } | ||
|
|
||
| export function getRateLimitResetTimeForFamily( | ||
| account: AccountRateLimitView, | ||
| now: number, | ||
| family: ModelFamily, | ||
| ): number | null { | ||
| const times = account.rateLimitResetTimes; | ||
| if (!times) return null; | ||
|
|
||
| let minReset: number | null = null; | ||
| const prefix = `${family}:`; | ||
| for (const [key, value] of Object.entries(times)) { | ||
| if (typeof value !== "number") continue; | ||
| if (value <= now) continue; | ||
| if (key !== family && !key.startsWith(prefix)) continue; | ||
| if (minReset === null || value < minReset) { | ||
| minReset = value; | ||
| } | ||
|
Comment on lines
+37
to
+43
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. filter out non-finite reset timestamps before selecting minimum. line 36 currently accepts proposed fix for (const [key, value] of Object.entries(times)) {
- if (typeof value !== "number") continue;
+ if (typeof value !== "number" || !Number.isFinite(value)) continue;
if (value <= now) continue;
if (key !== family && !key.startsWith(prefix)) continue;
if (minReset === null || value < minReset) {
minReset = value;
}
}please add regression coverage in As per coding guidelines, "lib/**: focus on auth rotation, windows filesystem IO, and concurrency. verify every change cites affected tests (vitest) and that new queues handle EBUSY/429 scenarios. check for logging that leaks tokens or emails." Also applies to: 52-56 🤖 Prompt for AI Agents |
||
| } | ||
|
|
||
| return minReset; | ||
| } | ||
|
|
||
| export function formatRateLimitEntry( | ||
| account: AccountRateLimitView, | ||
| now: number, | ||
| family: ModelFamily = "codex", | ||
| ): string | null { | ||
| const resetAt = getRateLimitResetTimeForFamily(account, now, family); | ||
| if (typeof resetAt !== "number") return null; | ||
| const remaining = resetAt - now; | ||
| if (remaining <= 0) return null; | ||
| return `resets in ${formatWaitTime(remaining)}`; | ||
| } | ||
|
|
||
| export function formatRateLimitStatusByFamily( | ||
| account: AccountRateLimitView, | ||
| now: number, | ||
| families: readonly ModelFamily[] = MODEL_FAMILIES, | ||
| ): string[] { | ||
| return families.map((family) => { | ||
| const resetAt = getRateLimitResetTimeForFamily(account, now, family); | ||
| if (typeof resetAt !== "number") return `${family}=ok`; | ||
| return `${family}=${formatWaitTime(resetAt - now)}`; | ||
| }); | ||
| } | ||
|
|
||
| export function formatActiveIndexByFamilyLabels( | ||
| activeIndexByFamily: ActiveIndexByFamilyView, | ||
| families: readonly ModelFamily[] = MODEL_FAMILIES, | ||
| ): string[] { | ||
| return families.map((family) => { | ||
| const idx = activeIndexByFamily?.[family]; | ||
| const label = typeof idx === "number" && Number.isFinite(idx) ? String(idx + 1) : "-"; | ||
| return `${family}: ${label}`; | ||
| }); | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
normalize active index to an integer before clamping.
line 22 in
lib/accounts/account-view.tscan return a fractional index (for example1.7). callers treat this as an array index inlib/codex-manager.ts:3340-3344andlib/codex-manager.ts:3937-3944, which can produceundefinedactive-account reads.proposed fix
export function resolveActiveIndex( storage: ActiveAccountStorageView, family: ModelFamily = "codex", ): number { const total = storage.accounts.length; if (total === 0) return 0; const rawCandidate = storage.activeIndexByFamily?.[family] ?? storage.activeIndex; - const raw = Number.isFinite(rawCandidate) ? rawCandidate : 0; - return Math.max(0, Math.min(raw, total - 1)); + const raw = Number.isFinite(rawCandidate) ? Math.floor(rawCandidate) : 0; + return Math.max(0, Math.min(raw, total - 1)); }please also add a regression in
test/account-view.test.tsfor fractionalactiveIndexandactiveIndexByFamilyvalues.As per coding guidelines, "lib/**: focus on auth rotation, windows filesystem IO, and concurrency. verify every change cites affected tests (vitest) and that new queues handle EBUSY/429 scenarios. check for logging that leaks tokens or emails."
📝 Committable suggestion
🤖 Prompt for AI Agents