oc-codex-multi-auth 6.3.4 → 6.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +18 -7
- package/dist/index.js.map +1 -1
- package/dist/lib/accounts/rotation.d.ts +30 -0
- package/dist/lib/accounts/rotation.d.ts.map +1 -1
- package/dist/lib/accounts/rotation.js +85 -28
- package/dist/lib/accounts/rotation.js.map +1 -1
- package/dist/lib/accounts/warm-request.d.ts +54 -0
- package/dist/lib/accounts/warm-request.d.ts.map +1 -0
- package/dist/lib/accounts/warm-request.js +134 -0
- package/dist/lib/accounts/warm-request.js.map +1 -0
- package/dist/lib/accounts/warm.d.ts +71 -0
- package/dist/lib/accounts/warm.d.ts.map +1 -0
- package/dist/lib/accounts/warm.js +86 -0
- package/dist/lib/accounts/warm.js.map +1 -0
- package/dist/lib/accounts.d.ts +9 -0
- package/dist/lib/accounts.d.ts.map +1 -1
- package/dist/lib/accounts.js +19 -0
- package/dist/lib/accounts.js.map +1 -1
- package/dist/lib/config.d.ts +17 -0
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/config.js +30 -0
- package/dist/lib/config.js.map +1 -1
- package/dist/lib/rotation.d.ts +15 -0
- package/dist/lib/rotation.d.ts.map +1 -1
- package/dist/lib/rotation.js +26 -0
- package/dist/lib/rotation.js.map +1 -1
- package/dist/lib/schemas.d.ts +5 -0
- package/dist/lib/schemas.d.ts.map +1 -1
- package/dist/lib/schemas.js +1 -0
- package/dist/lib/schemas.js.map +1 -1
- package/dist/lib/tools/codex-help.d.ts.map +1 -1
- package/dist/lib/tools/codex-help.js +1 -0
- package/dist/lib/tools/codex-help.js.map +1 -1
- package/dist/lib/tools/codex-warm.d.ts +33 -0
- package/dist/lib/tools/codex-warm.d.ts.map +1 -0
- package/dist/lib/tools/codex-warm.js +119 -0
- package/dist/lib/tools/codex-warm.js.map +1 -0
- package/dist/lib/tools/index.d.ts.map +1 -1
- package/dist/lib/tools/index.js +2 -0
- package/dist/lib/tools/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -14,9 +14,39 @@ import type { AccountState, ManagedAccount } from "./state.js";
|
|
|
14
14
|
export declare class AccountRotation {
|
|
15
15
|
private readonly state;
|
|
16
16
|
constructor(state: AccountState);
|
|
17
|
+
/**
|
|
18
|
+
* Whether an account can serve a request for this (family, model) right now:
|
|
19
|
+
* enabled, not rate-limited (real server 429s tracked in rateLimitResetTimes),
|
|
20
|
+
* not cooling down, AND its in-memory local token bucket has a token.
|
|
21
|
+
*
|
|
22
|
+
* The token-bucket check is what keeps a locally-depleted account out of
|
|
23
|
+
* selection. It is intentionally evaluated here (in-memory, per-process)
|
|
24
|
+
* rather than by writing a synthetic window into the persisted
|
|
25
|
+
* rateLimitResetTimes — that would leak a per-process proactive-limiter
|
|
26
|
+
* signal into the cross-process accounts file and spuriously rate-limit
|
|
27
|
+
* server-healthy accounts in other processes.
|
|
28
|
+
*/
|
|
29
|
+
private isSelectable;
|
|
17
30
|
getCurrentOrNextForFamily(family: ModelFamily, model?: string | null): ManagedAccount | null;
|
|
18
31
|
getNextForFamily(family: ModelFamily, model?: string | null): ManagedAccount | null;
|
|
19
32
|
getCurrentOrNextForFamilyHybrid(family: ModelFamily, model?: string | null, options?: HybridSelectionOptions): ManagedAccount | null;
|
|
33
|
+
/**
|
|
34
|
+
* Drain-first ("sticky") selection for issue #183.
|
|
35
|
+
*
|
|
36
|
+
* Stays on the current account for the family while it remains healthy
|
|
37
|
+
* (not disabled, not rate-limited for this family/model, not cooling down).
|
|
38
|
+
* When the current account is unavailable, it picks the *lowest-indexed*
|
|
39
|
+
* available account rather than spreading load. This concentrates traffic
|
|
40
|
+
* on as few accounts as possible so the remaining accounts keep their
|
|
41
|
+
* quota in reserve — staggering weekly-quota cooldowns instead of
|
|
42
|
+
* exhausting every account simultaneously (the round-robin failure mode the
|
|
43
|
+
* issue describes).
|
|
44
|
+
*
|
|
45
|
+
* Returns null when no account is available (every account disabled,
|
|
46
|
+
* rate-limited, or cooling down), matching the other selectors' contract so
|
|
47
|
+
* the request loop's wait/retry logic is unchanged.
|
|
48
|
+
*/
|
|
49
|
+
getCurrentOrNextForFamilySticky(family: ModelFamily, model?: string | null): ManagedAccount | null;
|
|
20
50
|
recordSuccess(account: ManagedAccount, family: ModelFamily, model?: string | null): void;
|
|
21
51
|
recordRateLimit(account: ManagedAccount, family: ModelFamily, model?: string | null): void;
|
|
22
52
|
recordFailure(account: ManagedAccount, family: ModelFamily, model?: string | null): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rotation.d.ts","sourceRoot":"","sources":["../../../lib/accounts/rotation.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AACvD,OAAO,EAKN,KAAK,sBAAsB,EAC3B,MAAM,gBAAgB,CAAC;AACxB,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAEpD,OAAO,EAIN,KAAK,eAAe,EACpB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,KAAK,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAE/D,qBAAa,eAAe;IACf,OAAO,CAAC,QAAQ,CAAC,KAAK;gBAAL,KAAK,EAAE,YAAY;IAEhD,yBAAyB,CACxB,MAAM,EAAE,WAAW,EACnB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,GACnB,cAAc,GAAG,IAAI;
|
|
1
|
+
{"version":3,"file":"rotation.d.ts","sourceRoot":"","sources":["../../../lib/accounts/rotation.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AACvD,OAAO,EAKN,KAAK,sBAAsB,EAC3B,MAAM,gBAAgB,CAAC;AACxB,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAEpD,OAAO,EAIN,KAAK,eAAe,EACpB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,KAAK,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAE/D,qBAAa,eAAe;IACf,OAAO,CAAC,QAAQ,CAAC,KAAK;gBAAL,KAAK,EAAE,YAAY;IAEhD;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,YAAY;IAapB,yBAAyB,CACxB,MAAM,EAAE,WAAW,EACnB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,GACnB,cAAc,GAAG,IAAI;IAqBxB,gBAAgB,CAAC,MAAM,EAAE,WAAW,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,cAAc,GAAG,IAAI;IAoBnF,+BAA+B,CAC9B,MAAM,EAAE,WAAW,EACnB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,EACrB,OAAO,CAAC,EAAE,sBAAsB,GAC9B,cAAc,GAAG,IAAI;IAoDxB;;;;;;;;;;;;;;;OAeG;IACH,+BAA+B,CAC9B,MAAM,EAAE,WAAW,EACnB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,GACnB,cAAc,GAAG,IAAI;IA8BxB,aAAa,CACZ,OAAO,EAAE,cAAc,EACvB,MAAM,EAAE,WAAW,EACnB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,GACnB,IAAI;IAKP,eAAe,CACd,OAAO,EAAE,cAAc,EACvB,MAAM,EAAE,WAAW,EACnB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,GACnB,IAAI;IAMP,aAAa,CACZ,OAAO,EAAE,cAAc,EACvB,MAAM,EAAE,WAAW,EACnB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,GACnB,IAAI;IAKP,YAAY,CACX,OAAO,EAAE,cAAc,EACvB,MAAM,EAAE,WAAW,EACnB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,GACnB,OAAO;IAKV;;;;OAIG;IACH,WAAW,CACV,OAAO,EAAE,cAAc,EACvB,MAAM,EAAE,WAAW,EACnB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,GACnB,OAAO;IAKV,eAAe,CACd,OAAO,EAAE,cAAc,EACvB,YAAY,EAAE,MAAM,EACpB,MAAM,EAAE,WAAW,EACnB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,GACnB,IAAI;IAIP,yBAAyB,CACxB,OAAO,EAAE,cAAc,EACvB,YAAY,EAAE,MAAM,EACpB,MAAM,EAAE,WAAW,EACnB,MAAM,EAAE,eAAe,EACvB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,GACnB,IAAI;IAeP,sBAAsB,CACrB,OAAO,EAAE,cAAc,EACvB,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,cAAc,GACpB,IAAI;IAMP;;;OAGG;IACH,uCAAuC,CACtC,YAAY,EAAE,MAAM,EACpB,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,cAAc,GACpB,MAAM;IAUT,uBAAuB,CAAC,MAAM,EAAE,WAAW,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM;CA+C3E"}
|
|
@@ -14,6 +14,29 @@ export class AccountRotation {
|
|
|
14
14
|
constructor(state) {
|
|
15
15
|
this.state = state;
|
|
16
16
|
}
|
|
17
|
+
/**
|
|
18
|
+
* Whether an account can serve a request for this (family, model) right now:
|
|
19
|
+
* enabled, not rate-limited (real server 429s tracked in rateLimitResetTimes),
|
|
20
|
+
* not cooling down, AND its in-memory local token bucket has a token.
|
|
21
|
+
*
|
|
22
|
+
* The token-bucket check is what keeps a locally-depleted account out of
|
|
23
|
+
* selection. It is intentionally evaluated here (in-memory, per-process)
|
|
24
|
+
* rather than by writing a synthetic window into the persisted
|
|
25
|
+
* rateLimitResetTimes — that would leak a per-process proactive-limiter
|
|
26
|
+
* signal into the cross-process accounts file and spuriously rate-limit
|
|
27
|
+
* server-healthy accounts in other processes.
|
|
28
|
+
*/
|
|
29
|
+
isSelectable(account, family, model) {
|
|
30
|
+
if (account.enabled === false)
|
|
31
|
+
return false;
|
|
32
|
+
clearExpiredRateLimits(account);
|
|
33
|
+
if (isRateLimitedForFamily(account, family, model))
|
|
34
|
+
return false;
|
|
35
|
+
if (this.state.isAccountCoolingDown(account))
|
|
36
|
+
return false;
|
|
37
|
+
const quotaKey = model ? `${family}:${model}` : family;
|
|
38
|
+
return getTokenTracker().hasToken(account.index, quotaKey);
|
|
39
|
+
}
|
|
17
40
|
getCurrentOrNextForFamily(family, model) {
|
|
18
41
|
const count = this.state.accounts.length;
|
|
19
42
|
if (count === 0)
|
|
@@ -24,13 +47,8 @@ export class AccountRotation {
|
|
|
24
47
|
const account = this.state.accounts[idx];
|
|
25
48
|
if (!account)
|
|
26
49
|
continue;
|
|
27
|
-
if (account
|
|
50
|
+
if (!this.isSelectable(account, family, model))
|
|
28
51
|
continue;
|
|
29
|
-
clearExpiredRateLimits(account);
|
|
30
|
-
if (isRateLimitedForFamily(account, family, model) ||
|
|
31
|
-
this.state.isAccountCoolingDown(account)) {
|
|
32
|
-
continue;
|
|
33
|
-
}
|
|
34
52
|
this.state.cursorByFamily[family] = (idx + 1) % count;
|
|
35
53
|
this.state.currentAccountIndexByFamily[family] = idx;
|
|
36
54
|
account.lastUsed = nowMs();
|
|
@@ -48,13 +66,8 @@ export class AccountRotation {
|
|
|
48
66
|
const account = this.state.accounts[idx];
|
|
49
67
|
if (!account)
|
|
50
68
|
continue;
|
|
51
|
-
if (account
|
|
52
|
-
continue;
|
|
53
|
-
clearExpiredRateLimits(account);
|
|
54
|
-
if (isRateLimitedForFamily(account, family, model) ||
|
|
55
|
-
this.state.isAccountCoolingDown(account)) {
|
|
69
|
+
if (!this.isSelectable(account, family, model))
|
|
56
70
|
continue;
|
|
57
|
-
}
|
|
58
71
|
this.state.cursorByFamily[family] = (idx + 1) % count;
|
|
59
72
|
account.lastUsed = nowMs();
|
|
60
73
|
return account;
|
|
@@ -72,13 +85,9 @@ export class AccountRotation {
|
|
|
72
85
|
if (currentAccount.enabled === false) {
|
|
73
86
|
// Fall through to hybrid selection.
|
|
74
87
|
}
|
|
75
|
-
else {
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
!this.state.isAccountCoolingDown(currentAccount)) {
|
|
79
|
-
currentAccount.lastUsed = nowMs();
|
|
80
|
-
return currentAccount;
|
|
81
|
-
}
|
|
88
|
+
else if (this.isSelectable(currentAccount, family, model)) {
|
|
89
|
+
currentAccount.lastUsed = nowMs();
|
|
90
|
+
return currentAccount;
|
|
82
91
|
}
|
|
83
92
|
}
|
|
84
93
|
}
|
|
@@ -91,12 +100,9 @@ export class AccountRotation {
|
|
|
91
100
|
return null;
|
|
92
101
|
if (account.enabled === false)
|
|
93
102
|
return null;
|
|
94
|
-
clearExpiredRateLimits(account);
|
|
95
|
-
const isAvailable = !isRateLimitedForFamily(account, family, model) &&
|
|
96
|
-
!this.state.isAccountCoolingDown(account);
|
|
97
103
|
return {
|
|
98
104
|
index: account.index,
|
|
99
|
-
isAvailable,
|
|
105
|
+
isAvailable: this.isSelectable(account, family, model),
|
|
100
106
|
lastUsed: account.lastUsed,
|
|
101
107
|
};
|
|
102
108
|
})
|
|
@@ -112,6 +118,50 @@ export class AccountRotation {
|
|
|
112
118
|
account.lastUsed = nowMs();
|
|
113
119
|
return account;
|
|
114
120
|
}
|
|
121
|
+
/**
|
|
122
|
+
* Drain-first ("sticky") selection for issue #183.
|
|
123
|
+
*
|
|
124
|
+
* Stays on the current account for the family while it remains healthy
|
|
125
|
+
* (not disabled, not rate-limited for this family/model, not cooling down).
|
|
126
|
+
* When the current account is unavailable, it picks the *lowest-indexed*
|
|
127
|
+
* available account rather than spreading load. This concentrates traffic
|
|
128
|
+
* on as few accounts as possible so the remaining accounts keep their
|
|
129
|
+
* quota in reserve — staggering weekly-quota cooldowns instead of
|
|
130
|
+
* exhausting every account simultaneously (the round-robin failure mode the
|
|
131
|
+
* issue describes).
|
|
132
|
+
*
|
|
133
|
+
* Returns null when no account is available (every account disabled,
|
|
134
|
+
* rate-limited, or cooling down), matching the other selectors' contract so
|
|
135
|
+
* the request loop's wait/retry logic is unchanged.
|
|
136
|
+
*/
|
|
137
|
+
getCurrentOrNextForFamilySticky(family, model) {
|
|
138
|
+
const count = this.state.accounts.length;
|
|
139
|
+
if (count === 0)
|
|
140
|
+
return null;
|
|
141
|
+
// Prefer the account we are already pinned to while it still has quota.
|
|
142
|
+
const currentIndex = this.state.currentAccountIndexByFamily[family];
|
|
143
|
+
if (currentIndex >= 0 && currentIndex < count) {
|
|
144
|
+
const currentAccount = this.state.accounts[currentIndex];
|
|
145
|
+
if (currentAccount && this.isSelectable(currentAccount, family, model)) {
|
|
146
|
+
currentAccount.lastUsed = nowMs();
|
|
147
|
+
return currentAccount;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
// Current account exhausted: pick the lowest-indexed available account so
|
|
151
|
+
// load concentrates rather than spreads.
|
|
152
|
+
for (let idx = 0; idx < count; idx++) {
|
|
153
|
+
const account = this.state.accounts[idx];
|
|
154
|
+
if (!account)
|
|
155
|
+
continue;
|
|
156
|
+
if (!this.isSelectable(account, family, model))
|
|
157
|
+
continue;
|
|
158
|
+
this.state.currentAccountIndexByFamily[family] = idx;
|
|
159
|
+
this.state.cursorByFamily[family] = (idx + 1) % count;
|
|
160
|
+
account.lastUsed = nowMs();
|
|
161
|
+
return account;
|
|
162
|
+
}
|
|
163
|
+
return null;
|
|
164
|
+
}
|
|
115
165
|
recordSuccess(account, family, model) {
|
|
116
166
|
const quotaKey = model ? `${family}:${model}` : family;
|
|
117
167
|
getHealthTracker().recordSuccess(account.index, quotaKey);
|
|
@@ -171,11 +221,7 @@ export class AccountRotation {
|
|
|
171
221
|
getMinWaitTimeForFamily(family, model) {
|
|
172
222
|
const now = nowMs();
|
|
173
223
|
const enabledAccounts = this.state.accounts.filter((account) => account.enabled !== false);
|
|
174
|
-
const available = enabledAccounts.filter((account) =>
|
|
175
|
-
clearExpiredRateLimits(account);
|
|
176
|
-
return (!isRateLimitedForFamily(account, family, model) &&
|
|
177
|
-
!this.state.isAccountCoolingDown(account));
|
|
178
|
-
});
|
|
224
|
+
const available = enabledAccounts.filter((account) => this.isSelectable(account, family, model));
|
|
179
225
|
if (available.length > 0)
|
|
180
226
|
return 0;
|
|
181
227
|
if (enabledAccounts.length === 0)
|
|
@@ -183,6 +229,8 @@ export class AccountRotation {
|
|
|
183
229
|
const waitTimes = [];
|
|
184
230
|
const baseKey = getQuotaKey(family);
|
|
185
231
|
const modelKey = model ? getQuotaKey(family, model) : null;
|
|
232
|
+
const tokenQuotaKey = model ? `${family}:${model}` : family;
|
|
233
|
+
const tokenTracker = getTokenTracker();
|
|
186
234
|
for (const account of enabledAccounts) {
|
|
187
235
|
const baseResetAt = account.rateLimitResetTimes[baseKey];
|
|
188
236
|
if (typeof baseResetAt === "number") {
|
|
@@ -197,6 +245,15 @@ export class AccountRotation {
|
|
|
197
245
|
if (typeof account.coolingDownUntil === "number") {
|
|
198
246
|
waitTimes.push(Math.max(0, account.coolingDownUntil - now));
|
|
199
247
|
}
|
|
248
|
+
// An account blocked only by a depleted local token bucket becomes
|
|
249
|
+
// available again after refill; include that wait so a fully
|
|
250
|
+
// token-depleted pool waits for refill instead of returning 0 (503).
|
|
251
|
+
if (account.enabled !== false) {
|
|
252
|
+
const tokenWait = tokenTracker.msUntilToken(account.index, tokenQuotaKey);
|
|
253
|
+
if (tokenWait > 0 && Number.isFinite(tokenWait)) {
|
|
254
|
+
waitTimes.push(tokenWait);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
200
257
|
}
|
|
201
258
|
return waitTimes.length > 0 ? Math.min(...waitTimes) : 0;
|
|
202
259
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rotation.js","sourceRoot":"","sources":["../../../lib/accounts/rotation.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,EACN,gBAAgB,EAChB,eAAe,EACf,mBAAmB,GAGnB,MAAM,gBAAgB,CAAC;AAExB,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AACpC,OAAO,EACN,sBAAsB,EACtB,WAAW,EACX,sBAAsB,GAEtB,MAAM,kBAAkB,CAAC;AAG1B,MAAM,OAAO,eAAe;IACE;IAA7B,YAA6B,KAAmB;QAAnB,UAAK,GAAL,KAAK,CAAc;IAAG,CAAC;IAEpD,yBAAyB,CACxB,MAAmB,EACnB,KAAqB;QAErB,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC;QACzC,IAAI,KAAK,KAAK,CAAC;YAAE,OAAO,IAAI,CAAC;QAE7B,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC;QAEjD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,EAAE,CAAC,EAAE,EAAE,CAAC;YAChC,MAAM,GAAG,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC,GAAG,KAAK,CAAC;YACjC,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;YACzC,IAAI,CAAC,OAAO;gBAAE,SAAS;YACvB,IAAI,
|
|
1
|
+
{"version":3,"file":"rotation.js","sourceRoot":"","sources":["../../../lib/accounts/rotation.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Real upstream "warm" request for issue #182.
|
|
3
|
+
*
|
|
4
|
+
* Sends a single minimal billable request to `POST /codex/responses` — the
|
|
5
|
+
* same endpoint and request shape the live request path uses for its quota
|
|
6
|
+
* probe — so the account's rolling usage window actually starts. A read-only
|
|
7
|
+
* `GET /wham/usage` does NOT open the window (it only reports server-side
|
|
8
|
+
* windows that already exist), so warming must send a genuine inference
|
|
9
|
+
* request. The body is deliberately tiny (reasoning effort "none", verbosity
|
|
10
|
+
* "low", no stored conversation) to keep the quota cost negligible, and mirrors
|
|
11
|
+
* the proven quota-probe request shape used by the live request path.
|
|
12
|
+
*
|
|
13
|
+
* This module owns the network side-effect; the pure iteration/summary logic
|
|
14
|
+
* lives in `warm.ts` and is injected this function via `codex-warm.ts`.
|
|
15
|
+
*/
|
|
16
|
+
import type { RequestBody } from "../types.js";
|
|
17
|
+
export interface WarmRequestParams {
|
|
18
|
+
accountId: string;
|
|
19
|
+
accessToken: string;
|
|
20
|
+
organizationId: string | undefined;
|
|
21
|
+
/** Override the timeout (tests). */
|
|
22
|
+
timeoutMs?: number;
|
|
23
|
+
/** Injectable fetch for tests; defaults to global fetch. */
|
|
24
|
+
fetchImpl?: typeof fetch;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Build the minimal warm-ping request body. Exported for tests so the exact
|
|
28
|
+
* shape (stream/store/reasoning) stays pinned.
|
|
29
|
+
*/
|
|
30
|
+
export declare function buildWarmRequestBody(model?: string): Promise<RequestBody>;
|
|
31
|
+
/**
|
|
32
|
+
* Outcome of a warm request.
|
|
33
|
+
* - `opened`: the request started/confirmed the usage window (2xx, or a 429
|
|
34
|
+
* whose reason is a transient token/concurrency limit — the window is ticking).
|
|
35
|
+
* - `exhausted`: a 429 whose reason is quota/usage-limit — the account's window
|
|
36
|
+
* is already spent, so warming it is meaningless. Reported distinctly so the
|
|
37
|
+
* tool does not claim a quota-dead account was "warmed".
|
|
38
|
+
*/
|
|
39
|
+
export type WarmRequestStatus = "opened" | "exhausted";
|
|
40
|
+
export interface WarmRequestResult {
|
|
41
|
+
status: WarmRequestStatus;
|
|
42
|
+
detail?: string;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Send one warm request to open the account's usage window.
|
|
46
|
+
*
|
|
47
|
+
* Resolves with `{ status: "opened" }` when the upstream started/confirmed the
|
|
48
|
+
* window (2xx, or a non-quota 429 meaning the window is already active), or
|
|
49
|
+
* `{ status: "exhausted" }` for a quota/usage-limit 429 (window already spent).
|
|
50
|
+
* Any other non-2xx, or a network/timeout error, throws so the caller records
|
|
51
|
+
* the account as failed.
|
|
52
|
+
*/
|
|
53
|
+
export declare function warmAccountWindow(params: WarmRequestParams): Promise<WarmRequestResult>;
|
|
54
|
+
//# sourceMappingURL=warm-request.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"warm-request.d.ts","sourceRoot":"","sources":["../../../lib/accounts/warm-request.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAMH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAW/C,MAAM,WAAW,iBAAiB;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,oCAAoC;IACpC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,4DAA4D;IAC5D,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;CACzB;AAED;;;GAGG;AACH,wBAAsB,oBAAoB,CAAC,KAAK,SAAa,GAAG,OAAO,CAAC,WAAW,CAAC,CAkBnF;AAED;;;;;;;GAOG;AACH,MAAM,MAAM,iBAAiB,GAAG,QAAQ,GAAG,WAAW,CAAC;AAEvD,MAAM,WAAW,iBAAiB;IACjC,MAAM,EAAE,iBAAiB,CAAC;IAC1B,MAAM,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;;GAQG;AACH,wBAAsB,iBAAiB,CACtC,MAAM,EAAE,iBAAiB,GACvB,OAAO,CAAC,iBAAiB,CAAC,CA4D5B"}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Real upstream "warm" request for issue #182.
|
|
3
|
+
*
|
|
4
|
+
* Sends a single minimal billable request to `POST /codex/responses` — the
|
|
5
|
+
* same endpoint and request shape the live request path uses for its quota
|
|
6
|
+
* probe — so the account's rolling usage window actually starts. A read-only
|
|
7
|
+
* `GET /wham/usage` does NOT open the window (it only reports server-side
|
|
8
|
+
* windows that already exist), so warming must send a genuine inference
|
|
9
|
+
* request. The body is deliberately tiny (reasoning effort "none", verbosity
|
|
10
|
+
* "low", no stored conversation) to keep the quota cost negligible, and mirrors
|
|
11
|
+
* the proven quota-probe request shape used by the live request path.
|
|
12
|
+
*
|
|
13
|
+
* This module owns the network side-effect; the pure iteration/summary logic
|
|
14
|
+
* lives in `warm.ts` and is injected this function via `codex-warm.ts`.
|
|
15
|
+
*/
|
|
16
|
+
import { CODEX_BASE_URL } from "../constants.js";
|
|
17
|
+
import { createCodexHeaders } from "../request/fetch-helpers.js";
|
|
18
|
+
import { getCodexInstructions } from "../prompts/codex.js";
|
|
19
|
+
import { parseRateLimitReason } from "./rate-limits.js";
|
|
20
|
+
import { createLogger } from "../logger.js";
|
|
21
|
+
const log = createLogger("warm-request");
|
|
22
|
+
/** Model used for the warm ping. Mirrors the live quota-probe default. */
|
|
23
|
+
const WARM_MODEL = "gpt-5.4";
|
|
24
|
+
/** Hard ceiling on a warm request so a hung upstream cannot wedge the batch. */
|
|
25
|
+
const WARM_TIMEOUT_MS = 15_000;
|
|
26
|
+
/**
|
|
27
|
+
* Build the minimal warm-ping request body. Exported for tests so the exact
|
|
28
|
+
* shape (stream/store/reasoning) stays pinned.
|
|
29
|
+
*/
|
|
30
|
+
export async function buildWarmRequestBody(model = WARM_MODEL) {
|
|
31
|
+
const instructions = await getCodexInstructions(model);
|
|
32
|
+
return {
|
|
33
|
+
model,
|
|
34
|
+
stream: true,
|
|
35
|
+
store: false,
|
|
36
|
+
include: ["reasoning.encrypted_content"],
|
|
37
|
+
instructions,
|
|
38
|
+
input: [
|
|
39
|
+
{
|
|
40
|
+
type: "message",
|
|
41
|
+
role: "user",
|
|
42
|
+
content: [{ type: "input_text", text: "warm ping" }],
|
|
43
|
+
},
|
|
44
|
+
],
|
|
45
|
+
reasoning: { effort: "none", summary: "auto" },
|
|
46
|
+
text: { verbosity: "low" },
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Send one warm request to open the account's usage window.
|
|
51
|
+
*
|
|
52
|
+
* Resolves with `{ status: "opened" }` when the upstream started/confirmed the
|
|
53
|
+
* window (2xx, or a non-quota 429 meaning the window is already active), or
|
|
54
|
+
* `{ status: "exhausted" }` for a quota/usage-limit 429 (window already spent).
|
|
55
|
+
* Any other non-2xx, or a network/timeout error, throws so the caller records
|
|
56
|
+
* the account as failed.
|
|
57
|
+
*/
|
|
58
|
+
export async function warmAccountWindow(params) {
|
|
59
|
+
const doFetch = params.fetchImpl ?? fetch;
|
|
60
|
+
const body = await buildWarmRequestBody();
|
|
61
|
+
const headers = createCodexHeaders(undefined, params.accountId, params.accessToken, {
|
|
62
|
+
model: WARM_MODEL,
|
|
63
|
+
organizationId: params.organizationId,
|
|
64
|
+
});
|
|
65
|
+
const controller = new AbortController();
|
|
66
|
+
const timeout = setTimeout(() => controller.abort(), params.timeoutMs ?? WARM_TIMEOUT_MS);
|
|
67
|
+
try {
|
|
68
|
+
const response = await doFetch(`${CODEX_BASE_URL}/codex/responses`, {
|
|
69
|
+
method: "POST",
|
|
70
|
+
headers,
|
|
71
|
+
body: JSON.stringify(body),
|
|
72
|
+
signal: controller.signal,
|
|
73
|
+
});
|
|
74
|
+
if (response.ok) {
|
|
75
|
+
// Drain/cancel the SSE stream — we only needed to start the window.
|
|
76
|
+
try {
|
|
77
|
+
await response.body?.cancel();
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
// Ignore cancellation failures.
|
|
81
|
+
}
|
|
82
|
+
return { status: "opened" };
|
|
83
|
+
}
|
|
84
|
+
// Read the error body (small) BEFORE classifying so a 429 quota-exhausted
|
|
85
|
+
// account is not mis-reported as warmed.
|
|
86
|
+
let bodyText = "";
|
|
87
|
+
try {
|
|
88
|
+
bodyText = (await response.text()).slice(0, 2048);
|
|
89
|
+
}
|
|
90
|
+
catch {
|
|
91
|
+
// Ignore body-read failures; fall back to status-only classification.
|
|
92
|
+
}
|
|
93
|
+
if (response.status === 429) {
|
|
94
|
+
const reason = parseRateLimitReason(extractErrorCode(bodyText));
|
|
95
|
+
if (reason === "quota") {
|
|
96
|
+
log.debug("Warm ping hit 429 quota limit — account window already spent", {
|
|
97
|
+
accountId: params.accountId,
|
|
98
|
+
});
|
|
99
|
+
return { status: "exhausted", detail: "quota/usage limit reached" };
|
|
100
|
+
}
|
|
101
|
+
// token/concurrent/unknown → the window is active and ticking.
|
|
102
|
+
log.debug("Warm ping hit 429 — window already active", {
|
|
103
|
+
accountId: params.accountId,
|
|
104
|
+
reason,
|
|
105
|
+
});
|
|
106
|
+
return { status: "opened" };
|
|
107
|
+
}
|
|
108
|
+
throw new Error(`Warm request failed: HTTP ${response.status}`);
|
|
109
|
+
}
|
|
110
|
+
finally {
|
|
111
|
+
clearTimeout(timeout);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Pull a rate-limit error code/message out of a JSON or plain-text error body
|
|
116
|
+
* so {@link parseRateLimitReason} can classify the 429. Best-effort: returns
|
|
117
|
+
* the raw text when JSON parsing fails so substring matching still works.
|
|
118
|
+
*/
|
|
119
|
+
function extractErrorCode(bodyText) {
|
|
120
|
+
if (!bodyText)
|
|
121
|
+
return undefined;
|
|
122
|
+
try {
|
|
123
|
+
const parsed = JSON.parse(bodyText);
|
|
124
|
+
return (parsed.error?.code ??
|
|
125
|
+
parsed.error?.type ??
|
|
126
|
+
parsed.error?.message ??
|
|
127
|
+
parsed.code ??
|
|
128
|
+
bodyText);
|
|
129
|
+
}
|
|
130
|
+
catch {
|
|
131
|
+
return bodyText;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
//# sourceMappingURL=warm-request.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"warm-request.js","sourceRoot":"","sources":["../../../lib/accounts/warm-request.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Account "warm-up" orchestrator (issue #182).
|
|
3
|
+
*
|
|
4
|
+
* Sends one minimal request to each enabled account so its rolling usage
|
|
5
|
+
* window (the ~5h ChatGPT/Codex quota window) starts ticking immediately,
|
|
6
|
+
* rather than only when the rotation eventually lands on that account. This
|
|
7
|
+
* lets users prime every account at the start of a session ("send a hi to each
|
|
8
|
+
* account") instead of waiting for the current account to run dry.
|
|
9
|
+
*
|
|
10
|
+
* This module is intentionally pure: it contains the iteration, outcome
|
|
11
|
+
* classification, and summary logic, but performs no network or auth I/O
|
|
12
|
+
* itself. The real upstream call is injected as `warmOne`, which keeps this
|
|
13
|
+
* logic fully deterministic and unit-testable, and keeps the network-touching
|
|
14
|
+
* adapter isolated in the `codex-warm` tool. The split mirrors how
|
|
15
|
+
* `lib/rotation.ts` (pure selection) is separated from `lib/accounts/rotation.ts`
|
|
16
|
+
* (the manager-facing wrapper).
|
|
17
|
+
*/
|
|
18
|
+
/**
|
|
19
|
+
* Per-account result of a warm attempt.
|
|
20
|
+
*
|
|
21
|
+
* - `warmed`: the upstream request succeeded; the window is now open.
|
|
22
|
+
* - `failed`: the upstream request was attempted but errored (auth invalid,
|
|
23
|
+
* network error, upstream 5xx, etc.). `detail` carries a short reason.
|
|
24
|
+
* - `skipped`: the account was not attempted (e.g. disabled). `detail` says why.
|
|
25
|
+
*/
|
|
26
|
+
export type WarmStatus = "warmed" | "failed" | "skipped";
|
|
27
|
+
export interface WarmOutcome {
|
|
28
|
+
status: WarmStatus;
|
|
29
|
+
/** Short human-readable reason, primarily for `failed` / `skipped`. */
|
|
30
|
+
detail?: string;
|
|
31
|
+
}
|
|
32
|
+
/** A warm result paired with the account it belongs to. */
|
|
33
|
+
export interface WarmAccountResult<A> {
|
|
34
|
+
account: A;
|
|
35
|
+
/** Zero-based position in the input list (stable for display ordering). */
|
|
36
|
+
index: number;
|
|
37
|
+
status: WarmStatus;
|
|
38
|
+
detail?: string;
|
|
39
|
+
}
|
|
40
|
+
export interface WarmSummary<A> {
|
|
41
|
+
results: WarmAccountResult<A>[];
|
|
42
|
+
warmedCount: number;
|
|
43
|
+
failedCount: number;
|
|
44
|
+
skippedCount: number;
|
|
45
|
+
/** Total accounts considered (= results.length). */
|
|
46
|
+
total: number;
|
|
47
|
+
}
|
|
48
|
+
export interface WarmAccountsOptions {
|
|
49
|
+
/**
|
|
50
|
+
* Run warm attempts concurrently (default) or strictly one at a time.
|
|
51
|
+
* Sequential mode is useful when the caller wants to avoid bursting the
|
|
52
|
+
* upstream with parallel requests. Defaults to `true` (concurrent).
|
|
53
|
+
*/
|
|
54
|
+
concurrent?: boolean;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Warm every account in `accounts` by invoking `warmOne` on each one that is
|
|
58
|
+
* eligible, then summarize the outcomes.
|
|
59
|
+
*
|
|
60
|
+
* `warmOne` must resolve (never reject) — but to be defensive against an
|
|
61
|
+
* injected function that throws or rejects, any thrown error is captured and
|
|
62
|
+
* recorded as a `failed` outcome so a single bad account can never abort the
|
|
63
|
+
* whole batch.
|
|
64
|
+
*
|
|
65
|
+
* The returned `results` array preserves the input order regardless of
|
|
66
|
+
* concurrency, so display output is deterministic.
|
|
67
|
+
*/
|
|
68
|
+
export declare function warmAccounts<A extends {
|
|
69
|
+
enabled?: boolean;
|
|
70
|
+
}>(accounts: readonly A[], warmOne: (account: A, index: number) => Promise<WarmOutcome>, options?: WarmAccountsOptions): Promise<WarmSummary<A>>;
|
|
71
|
+
//# sourceMappingURL=warm.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"warm.d.ts","sourceRoot":"","sources":["../../../lib/accounts/warm.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH;;;;;;;GAOG;AACH,MAAM,MAAM,UAAU,GAAG,QAAQ,GAAG,QAAQ,GAAG,SAAS,CAAC;AAEzD,MAAM,WAAW,WAAW;IAC3B,MAAM,EAAE,UAAU,CAAC;IACnB,uEAAuE;IACvE,MAAM,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,2DAA2D;AAC3D,MAAM,WAAW,iBAAiB,CAAC,CAAC;IACnC,OAAO,EAAE,CAAC,CAAC;IACX,2EAA2E;IAC3E,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,UAAU,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,WAAW,CAAC,CAAC;IAC7B,OAAO,EAAE,iBAAiB,CAAC,CAAC,CAAC,EAAE,CAAC;IAChC,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,oDAAoD;IACpD,KAAK,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,mBAAmB;IACnC;;;;OAIG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;CACrB;AAUD;;;;;;;;;;;GAWG;AACH,wBAAsB,YAAY,CAAC,CAAC,SAAS;IAAE,OAAO,CAAC,EAAE,OAAO,CAAA;CAAE,EACjE,QAAQ,EAAE,SAAS,CAAC,EAAE,EACtB,OAAO,EAAE,CAAC,OAAO,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,WAAW,CAAC,EAC5D,OAAO,GAAE,mBAAwB,GAC/B,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAgDzB"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Account "warm-up" orchestrator (issue #182).
|
|
3
|
+
*
|
|
4
|
+
* Sends one minimal request to each enabled account so its rolling usage
|
|
5
|
+
* window (the ~5h ChatGPT/Codex quota window) starts ticking immediately,
|
|
6
|
+
* rather than only when the rotation eventually lands on that account. This
|
|
7
|
+
* lets users prime every account at the start of a session ("send a hi to each
|
|
8
|
+
* account") instead of waiting for the current account to run dry.
|
|
9
|
+
*
|
|
10
|
+
* This module is intentionally pure: it contains the iteration, outcome
|
|
11
|
+
* classification, and summary logic, but performs no network or auth I/O
|
|
12
|
+
* itself. The real upstream call is injected as `warmOne`, which keeps this
|
|
13
|
+
* logic fully deterministic and unit-testable, and keeps the network-touching
|
|
14
|
+
* adapter isolated in the `codex-warm` tool. The split mirrors how
|
|
15
|
+
* `lib/rotation.ts` (pure selection) is separated from `lib/accounts/rotation.ts`
|
|
16
|
+
* (the manager-facing wrapper).
|
|
17
|
+
*/
|
|
18
|
+
/**
|
|
19
|
+
* Decide whether an account should be warmed at all.
|
|
20
|
+
* Disabled accounts are skipped; everything else is attempted.
|
|
21
|
+
*/
|
|
22
|
+
function shouldSkip(account) {
|
|
23
|
+
return account.enabled === false;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Warm every account in `accounts` by invoking `warmOne` on each one that is
|
|
27
|
+
* eligible, then summarize the outcomes.
|
|
28
|
+
*
|
|
29
|
+
* `warmOne` must resolve (never reject) — but to be defensive against an
|
|
30
|
+
* injected function that throws or rejects, any thrown error is captured and
|
|
31
|
+
* recorded as a `failed` outcome so a single bad account can never abort the
|
|
32
|
+
* whole batch.
|
|
33
|
+
*
|
|
34
|
+
* The returned `results` array preserves the input order regardless of
|
|
35
|
+
* concurrency, so display output is deterministic.
|
|
36
|
+
*/
|
|
37
|
+
export async function warmAccounts(accounts, warmOne, options = {}) {
|
|
38
|
+
const concurrent = options.concurrent ?? true;
|
|
39
|
+
const runOne = async (account, index) => {
|
|
40
|
+
if (shouldSkip(account)) {
|
|
41
|
+
return { account, index, status: "skipped", detail: "disabled" };
|
|
42
|
+
}
|
|
43
|
+
try {
|
|
44
|
+
const outcome = await warmOne(account, index);
|
|
45
|
+
return { account, index, status: outcome.status, detail: outcome.detail };
|
|
46
|
+
}
|
|
47
|
+
catch (error) {
|
|
48
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
49
|
+
return { account, index, status: "failed", detail };
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
let results;
|
|
53
|
+
if (concurrent) {
|
|
54
|
+
results = await Promise.all(accounts.map((account, index) => runOne(account, index)));
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
results = [];
|
|
58
|
+
for (let index = 0; index < accounts.length; index++) {
|
|
59
|
+
// Non-null: index is bounded by accounts.length.
|
|
60
|
+
const account = accounts[index];
|
|
61
|
+
results.push(await runOne(account, index));
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
// Preserve input order even though Promise.all already does; explicit for
|
|
65
|
+
// safety against future refactors to the concurrent branch.
|
|
66
|
+
results.sort((a, b) => a.index - b.index);
|
|
67
|
+
let warmedCount = 0;
|
|
68
|
+
let failedCount = 0;
|
|
69
|
+
let skippedCount = 0;
|
|
70
|
+
for (const result of results) {
|
|
71
|
+
if (result.status === "warmed")
|
|
72
|
+
warmedCount++;
|
|
73
|
+
else if (result.status === "failed")
|
|
74
|
+
failedCount++;
|
|
75
|
+
else
|
|
76
|
+
skippedCount++;
|
|
77
|
+
}
|
|
78
|
+
return {
|
|
79
|
+
results,
|
|
80
|
+
warmedCount,
|
|
81
|
+
failedCount,
|
|
82
|
+
skippedCount,
|
|
83
|
+
total: results.length,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
//# sourceMappingURL=warm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"warm.js","sourceRoot":"","sources":["../../../lib/accounts/warm.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AA6CH;;;GAGG;AACH,SAAS,UAAU,CAAkC,OAAU;IAC9D,OAAO,OAAO,CAAC,OAAO,KAAK,KAAK,CAAC;AAClC,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CACjC,QAAsB,EACtB,OAA4D,EAC5D,UAA+B,EAAE;IAEjC,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,IAAI,IAAI,CAAC;IAE9C,MAAM,MAAM,GAAG,KAAK,EAAE,OAAU,EAAE,KAAa,EAAiC,EAAE;QACjF,IAAI,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;YACzB,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC;QAClE,CAAC;QACD,IAAI,CAAC;YACJ,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;YAC9C,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC;QAC3E,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,MAAM,MAAM,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YACtE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC;QACrD,CAAC;IACF,CAAC,CAAC;IAEF,IAAI,OAA+B,CAAC;IACpC,IAAI,UAAU,EAAE,CAAC;QAChB,OAAO,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,KAAK,EAAE,EAAE,CAAC,MAAM,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC;IACvF,CAAC;SAAM,CAAC;QACP,OAAO,GAAG,EAAE,CAAC;QACb,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,QAAQ,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,CAAC;YACtD,iDAAiD;YACjD,MAAM,OAAO,GAAG,QAAQ,CAAC,KAAK,CAAM,CAAC;YACrC,OAAO,CAAC,IAAI,CAAC,MAAM,MAAM,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC;QAC5C,CAAC;IACF,CAAC;IAED,0EAA0E;IAC1E,4DAA4D;IAC5D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC;IAE1C,IAAI,WAAW,GAAG,CAAC,CAAC;IACpB,IAAI,WAAW,GAAG,CAAC,CAAC;IACpB,IAAI,YAAY,GAAG,CAAC,CAAC;IACrB,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC9B,IAAI,MAAM,CAAC,MAAM,KAAK,QAAQ;YAAE,WAAW,EAAE,CAAC;aACzC,IAAI,MAAM,CAAC,MAAM,KAAK,QAAQ;YAAE,WAAW,EAAE,CAAC;;YAC9C,YAAY,EAAE,CAAC;IACrB,CAAC;IAED,OAAO;QACN,OAAO;QACP,WAAW;QACX,WAAW;QACX,YAAY;QACZ,KAAK,EAAE,OAAO,CAAC,MAAM;KACrB,CAAC;AACH,CAAC"}
|