-
Notifications
You must be signed in to change notification settings - Fork 34
Expand file tree
/
Copy pathforecast-report-shared.ts
More file actions
121 lines (115 loc) · 3.66 KB
/
Copy pathforecast-report-shared.ts
File metadata and controls
121 lines (115 loc) · 3.66 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
import type { ForecastAccountResult } from "../forecast.js";
import type { CodexQuotaSnapshot } from "../quota-probe.js";
import {
findMatchingAccountIndex,
type AccountMetadataV3,
type AccountStorageV3,
} from "../storage.js";
import { saveAccountsWithRetry } from "../storage/save-retry.js";
import type { TokenFailure } from "../types.js";
// Moved to lib/storage/save-retry.ts so lib/accounts.ts can use the retry
// helper without importing this codex-manager module; re-exported here to
// preserve the historical import surface.
export { saveAccountsWithRetry };
export type AccountIdentityMatch = Pick<
AccountMetadataV3,
"accountId" | "email" | "refreshToken"
>;
export type RefreshedAccountPatch = Pick<
AccountMetadataV3,
"refreshToken" | "accessToken" | "expiresAt"
> & {
email?: AccountMetadataV3["email"];
accountId?: AccountMetadataV3["accountId"];
accountIdSource?: AccountMetadataV3["accountIdSource"];
};
export function applyRefreshedAccountPatch(
account: AccountMetadataV3,
patch: RefreshedAccountPatch,
): void {
account.refreshToken = patch.refreshToken;
account.accessToken = patch.accessToken;
account.expiresAt = patch.expiresAt;
if (patch.email) account.email = patch.email;
if (patch.accountId) {
account.accountId = patch.accountId;
account.accountIdSource = patch.accountIdSource;
}
}
export async function persistRefreshedAccountPatch(
storage: AccountStorageV3,
accountMatch: AccountIdentityMatch,
patch: RefreshedAccountPatch,
loadAccounts: () => Promise<AccountStorageV3 | null>,
saveAccounts: (storage: AccountStorageV3) => Promise<void>,
): Promise<void> {
const latestStorage = (await loadAccounts()) ?? storage;
const nextStorage = structuredClone(latestStorage);
const targetIndex =
findMatchingAccountIndex(nextStorage.accounts, accountMatch, {
allowUniqueAccountIdFallbackWithoutEmail: true,
}) ??
findMatchingAccountIndex(nextStorage.accounts, patch, {
allowUniqueAccountIdFallbackWithoutEmail: true,
});
if (targetIndex === undefined) {
throw new Error("Unable to resolve refreshed account for persistence");
}
const targetAccount = nextStorage.accounts[targetIndex];
if (!targetAccount) {
throw new Error("Unable to resolve refreshed account for persistence");
}
applyRefreshedAccountPatch(targetAccount, patch);
await saveAccountsWithRetry(nextStorage, saveAccounts);
}
export function serializeForecastResults(
results: ForecastAccountResult[],
liveQuotaByIndex: Map<number, CodexQuotaSnapshot>,
refreshFailures: Map<number, TokenFailure>,
formatQuotaSnapshotLine: (snapshot: CodexQuotaSnapshot) => string,
): Array<{
index: number;
label: string;
isCurrent: boolean;
selected: boolean;
primaryReason?: string;
availability: ForecastAccountResult["availability"];
riskScore: number;
riskLevel: ForecastAccountResult["riskLevel"];
waitMs: number;
reasons: string[];
liveQuota?: {
status: number;
planType?: string;
activeLimit?: number;
model: string;
summary: string;
};
refreshFailure?: TokenFailure;
}> {
return results.map((result) => {
const liveQuota = liveQuotaByIndex.get(result.index);
return {
index: result.index,
label: result.label,
isCurrent: result.isCurrent,
selected: false,
primaryReason: result.reasons[0],
availability: result.availability,
riskScore: result.riskScore,
riskLevel: result.riskLevel,
waitMs: result.waitMs,
reasons: result.reasons,
liveQuota: liveQuota
? {
status: liveQuota.status,
planType: liveQuota.planType,
activeLimit: liveQuota.activeLimit,
model: liveQuota.model,
summary: formatQuotaSnapshotLine(liveQuota),
}
: undefined,
refreshFailure: refreshFailures.get(result.index),
};
});
}