codex-multi-auth 1.2.7 → 1.3.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.
Files changed (160) hide show
  1. package/README.md +5 -3
  2. package/config/codex-legacy.json +85 -10
  3. package/config/codex-modern.json +35 -2
  4. package/dist/index.d.ts.map +1 -1
  5. package/dist/index.js +49 -22
  6. package/dist/index.js.map +1 -1
  7. package/dist/lib/accounts.d.ts +47 -0
  8. package/dist/lib/accounts.d.ts.map +1 -1
  9. package/dist/lib/accounts.js +277 -33
  10. package/dist/lib/accounts.js.map +1 -1
  11. package/dist/lib/auth/auth.d.ts +35 -0
  12. package/dist/lib/auth/auth.d.ts.map +1 -1
  13. package/dist/lib/auth/auth.js +140 -8
  14. package/dist/lib/auth/auth.js.map +1 -1
  15. package/dist/lib/auth/server.d.ts +1 -1
  16. package/dist/lib/auth/server.d.ts.map +1 -1
  17. package/dist/lib/auth/server.js +19 -11
  18. package/dist/lib/auth/server.js.map +1 -1
  19. package/dist/lib/capability-policy.d.ts.map +1 -1
  20. package/dist/lib/capability-policy.js +7 -2
  21. package/dist/lib/capability-policy.js.map +1 -1
  22. package/dist/lib/codex-manager/commands/status.d.ts.map +1 -1
  23. package/dist/lib/codex-manager/commands/status.js +13 -5
  24. package/dist/lib/codex-manager/commands/status.js.map +1 -1
  25. package/dist/lib/codex-manager/commands/verify.d.ts +59 -0
  26. package/dist/lib/codex-manager/commands/verify.d.ts.map +1 -0
  27. package/dist/lib/codex-manager/commands/verify.js +332 -0
  28. package/dist/lib/codex-manager/commands/verify.js.map +1 -0
  29. package/dist/lib/codex-manager/commands/why-selected.d.ts +57 -0
  30. package/dist/lib/codex-manager/commands/why-selected.d.ts.map +1 -0
  31. package/dist/lib/codex-manager/commands/why-selected.js +198 -0
  32. package/dist/lib/codex-manager/commands/why-selected.js.map +1 -0
  33. package/dist/lib/codex-manager/help.d.ts.map +1 -1
  34. package/dist/lib/codex-manager/help.js +4 -0
  35. package/dist/lib/codex-manager/help.js.map +1 -1
  36. package/dist/lib/codex-manager/settings-hub/backend.d.ts +9 -0
  37. package/dist/lib/codex-manager/settings-hub/backend.d.ts.map +1 -0
  38. package/dist/lib/codex-manager/settings-hub/backend.js +79 -0
  39. package/dist/lib/codex-manager/settings-hub/backend.js.map +1 -0
  40. package/dist/lib/codex-manager/settings-hub/dashboard.d.ts +24 -0
  41. package/dist/lib/codex-manager/settings-hub/dashboard.d.ts.map +1 -0
  42. package/dist/lib/codex-manager/settings-hub/dashboard.js +223 -0
  43. package/dist/lib/codex-manager/settings-hub/dashboard.js.map +1 -0
  44. package/dist/lib/codex-manager/settings-hub/experimental.d.ts +18 -0
  45. package/dist/lib/codex-manager/settings-hub/experimental.d.ts.map +1 -0
  46. package/dist/lib/codex-manager/settings-hub/experimental.js +78 -0
  47. package/dist/lib/codex-manager/settings-hub/experimental.js.map +1 -0
  48. package/dist/lib/codex-manager/settings-hub/index.d.ts +34 -0
  49. package/dist/lib/codex-manager/settings-hub/index.d.ts.map +1 -0
  50. package/dist/lib/codex-manager/settings-hub/index.js +81 -0
  51. package/dist/lib/codex-manager/settings-hub/index.js.map +1 -0
  52. package/dist/lib/codex-manager/settings-hub/shared.d.ts +40 -0
  53. package/dist/lib/codex-manager/settings-hub/shared.d.ts.map +1 -0
  54. package/dist/lib/codex-manager/settings-hub/shared.js +103 -0
  55. package/dist/lib/codex-manager/settings-hub/shared.js.map +1 -0
  56. package/dist/lib/codex-manager/settings-hub.d.ts +1 -64
  57. package/dist/lib/codex-manager/settings-hub.d.ts.map +1 -1
  58. package/dist/lib/codex-manager/settings-hub.js +4 -515
  59. package/dist/lib/codex-manager/settings-hub.js.map +1 -1
  60. package/dist/lib/codex-manager.d.ts.map +1 -1
  61. package/dist/lib/codex-manager.js +97 -15
  62. package/dist/lib/codex-manager.js.map +1 -1
  63. package/dist/lib/config.d.ts +14 -0
  64. package/dist/lib/config.d.ts.map +1 -1
  65. package/dist/lib/config.js +21 -9
  66. package/dist/lib/config.js.map +1 -1
  67. package/dist/lib/health.d.ts.map +1 -1
  68. package/dist/lib/health.js.map +1 -1
  69. package/dist/lib/prompts/codex.d.ts +2 -2
  70. package/dist/lib/prompts/codex.js +2 -2
  71. package/dist/lib/recovery/storage.d.ts +13 -0
  72. package/dist/lib/recovery/storage.d.ts.map +1 -1
  73. package/dist/lib/recovery/storage.js +144 -9
  74. package/dist/lib/recovery/storage.js.map +1 -1
  75. package/dist/lib/request/fetch-helpers.d.ts.map +1 -1
  76. package/dist/lib/request/fetch-helpers.js +15 -2
  77. package/dist/lib/request/fetch-helpers.js.map +1 -1
  78. package/dist/lib/request/helpers/model-map.d.ts +3 -3
  79. package/dist/lib/request/helpers/model-map.d.ts.map +1 -1
  80. package/dist/lib/request/helpers/model-map.js +146 -70
  81. package/dist/lib/request/helpers/model-map.js.map +1 -1
  82. package/dist/lib/request/rate-limit-backoff.d.ts +14 -1
  83. package/dist/lib/request/rate-limit-backoff.d.ts.map +1 -1
  84. package/dist/lib/request/rate-limit-backoff.js +26 -5
  85. package/dist/lib/request/rate-limit-backoff.js.map +1 -1
  86. package/dist/lib/request/response-handler.d.ts.map +1 -1
  87. package/dist/lib/request/response-handler.js +82 -25
  88. package/dist/lib/request/response-handler.js.map +1 -1
  89. package/dist/lib/rotation.d.ts +55 -1
  90. package/dist/lib/rotation.d.ts.map +1 -1
  91. package/dist/lib/rotation.js +161 -23
  92. package/dist/lib/rotation.js.map +1 -1
  93. package/dist/lib/routing-mutex.d.ts +107 -0
  94. package/dist/lib/routing-mutex.d.ts.map +1 -0
  95. package/dist/lib/routing-mutex.js +104 -0
  96. package/dist/lib/routing-mutex.js.map +1 -0
  97. package/dist/lib/runtime/storage-scope.d.ts.map +1 -1
  98. package/dist/lib/runtime/storage-scope.js +17 -1
  99. package/dist/lib/runtime/storage-scope.js.map +1 -1
  100. package/dist/lib/schemas.d.ts +125 -0
  101. package/dist/lib/schemas.d.ts.map +1 -1
  102. package/dist/lib/schemas.js +139 -5
  103. package/dist/lib/schemas.js.map +1 -1
  104. package/dist/lib/shutdown.d.ts.map +1 -1
  105. package/dist/lib/shutdown.js +19 -10
  106. package/dist/lib/shutdown.js.map +1 -1
  107. package/dist/lib/storage/account-clear.d.ts.map +1 -1
  108. package/dist/lib/storage/account-clear.js +7 -1
  109. package/dist/lib/storage/account-clear.js.map +1 -1
  110. package/dist/lib/storage/flagged-storage-file.d.ts.map +1 -1
  111. package/dist/lib/storage/flagged-storage-file.js +17 -1
  112. package/dist/lib/storage/flagged-storage-file.js.map +1 -1
  113. package/dist/lib/storage/flagged-storage-io.d.ts.map +1 -1
  114. package/dist/lib/storage/flagged-storage-io.js +24 -10
  115. package/dist/lib/storage/flagged-storage-io.js.map +1 -1
  116. package/dist/lib/storage/import-export.d.ts.map +1 -1
  117. package/dist/lib/storage/import-export.js +31 -5
  118. package/dist/lib/storage/import-export.js.map +1 -1
  119. package/dist/lib/storage/paths.d.ts.map +1 -1
  120. package/dist/lib/storage/paths.js +62 -7
  121. package/dist/lib/storage/paths.js.map +1 -1
  122. package/dist/lib/storage/snapshot-inspectors.d.ts.map +1 -1
  123. package/dist/lib/storage/snapshot-inspectors.js +23 -16
  124. package/dist/lib/storage/snapshot-inspectors.js.map +1 -1
  125. package/dist/lib/storage/storage-parser.d.ts +17 -0
  126. package/dist/lib/storage/storage-parser.d.ts.map +1 -1
  127. package/dist/lib/storage/storage-parser.js +30 -1
  128. package/dist/lib/storage/storage-parser.js.map +1 -1
  129. package/dist/lib/storage.d.ts +1 -1
  130. package/dist/lib/storage.d.ts.map +1 -1
  131. package/dist/lib/storage.js +45 -26
  132. package/dist/lib/storage.js.map +1 -1
  133. package/dist/lib/ui/copy.d.ts +1 -1
  134. package/dist/lib/ui/copy.d.ts.map +1 -1
  135. package/dist/lib/ui/copy.js +2 -1
  136. package/dist/lib/ui/copy.js.map +1 -1
  137. package/dist/lib/ui/select.d.ts.map +1 -1
  138. package/dist/lib/ui/select.js +5 -1
  139. package/dist/lib/ui/select.js.map +1 -1
  140. package/dist/lib/unified-settings.d.ts.map +1 -1
  141. package/dist/lib/unified-settings.js +69 -14
  142. package/dist/lib/unified-settings.js.map +1 -1
  143. package/package.json +5 -5
  144. package/scripts/codex.js +330 -65
  145. package/dist/lib/codex-manager/auth-commands.d.ts +0 -72
  146. package/dist/lib/codex-manager/auth-commands.d.ts.map +0 -1
  147. package/dist/lib/codex-manager/auth-commands.js +0 -643
  148. package/dist/lib/codex-manager/auth-commands.js.map +0 -1
  149. package/dist/lib/codex-manager/commands/doctor.d.ts +0 -85
  150. package/dist/lib/codex-manager/commands/doctor.d.ts.map +0 -1
  151. package/dist/lib/codex-manager/commands/doctor.js +0 -424
  152. package/dist/lib/codex-manager/commands/doctor.js.map +0 -1
  153. package/dist/lib/codex-manager/commands/fix.d.ts +0 -92
  154. package/dist/lib/codex-manager/commands/fix.d.ts.map +0 -1
  155. package/dist/lib/codex-manager/commands/fix.js +0 -347
  156. package/dist/lib/codex-manager/commands/fix.js.map +0 -1
  157. package/dist/lib/codex-manager/commands/verify-flagged.d.ts +0 -69
  158. package/dist/lib/codex-manager/commands/verify-flagged.d.ts.map +0 -1
  159. package/dist/lib/codex-manager/commands/verify-flagged.js +0 -251
  160. package/dist/lib/codex-manager/commands/verify-flagged.js.map +0 -1
@@ -6,6 +6,7 @@
6
6
  * when rate limits are encountered.
7
7
  */
8
8
  import { createLogger } from "./logger.js";
9
+ export { createAsyncMutex, getRoutingMutex, withRoutingMutex, } from "./routing-mutex.js";
9
10
  const log = createLogger("rotation");
10
11
  export const DEFAULT_HEALTH_SCORE_CONFIG = {
11
12
  successDelta: 1,
@@ -53,7 +54,9 @@ export class HealthScoreTracker {
53
54
  recordSuccess(accountKey, quotaKey) {
54
55
  const key = this.getKey(accountKey, quotaKey);
55
56
  const entry = this.entries.get(key);
56
- const baseScore = entry ? this.applyPassiveRecovery(entry) : this.config.maxScore;
57
+ const baseScore = entry
58
+ ? this.applyPassiveRecovery(entry)
59
+ : this.config.maxScore;
57
60
  const newScore = Math.min(baseScore + this.config.successDelta, this.config.maxScore);
58
61
  this.entries.set(key, {
59
62
  score: newScore,
@@ -64,7 +67,9 @@ export class HealthScoreTracker {
64
67
  recordRateLimit(accountKey, quotaKey) {
65
68
  const key = this.getKey(accountKey, quotaKey);
66
69
  const entry = this.entries.get(key);
67
- const baseScore = entry ? this.applyPassiveRecovery(entry) : this.config.maxScore;
70
+ const baseScore = entry
71
+ ? this.applyPassiveRecovery(entry)
72
+ : this.config.maxScore;
68
73
  const newScore = Math.max(baseScore + this.config.rateLimitDelta, this.config.minScore);
69
74
  this.entries.set(key, {
70
75
  score: newScore,
@@ -75,7 +80,9 @@ export class HealthScoreTracker {
75
80
  recordFailure(accountKey, quotaKey) {
76
81
  const key = this.getKey(accountKey, quotaKey);
77
82
  const entry = this.entries.get(key);
78
- const baseScore = entry ? this.applyPassiveRecovery(entry) : this.config.maxScore;
83
+ const baseScore = entry
84
+ ? this.applyPassiveRecovery(entry)
85
+ : this.config.maxScore;
79
86
  const newScore = Math.max(baseScore + this.config.failureDelta, this.config.minScore);
80
87
  this.entries.set(key, {
81
88
  score: newScore,
@@ -90,6 +97,19 @@ export class HealthScoreTracker {
90
97
  clear() {
91
98
  this.entries.clear();
92
99
  }
100
+ clearNumericKeysAtOrAbove(startIndex) {
101
+ for (const key of this.entries.keys()) {
102
+ try {
103
+ const [accountKey] = JSON.parse(key);
104
+ if (/^\d+$/.test(accountKey) && Number(accountKey) >= startIndex) {
105
+ this.entries.delete(key);
106
+ }
107
+ }
108
+ catch {
109
+ // Ignore malformed tracker keys.
110
+ }
111
+ }
112
+ }
93
113
  }
94
114
  export const DEFAULT_TOKEN_BUCKET_CONFIG = {
95
115
  maxTokens: 50,
@@ -128,7 +148,9 @@ export class TokenBucketTracker {
128
148
  tryConsume(accountKey, quotaKey) {
129
149
  const key = this.getKey(accountKey, quotaKey);
130
150
  const entry = this.buckets.get(key);
131
- const currentTokens = entry ? this.refillTokens(entry) : this.config.maxTokens;
151
+ const currentTokens = entry
152
+ ? this.refillTokens(entry)
153
+ : this.config.maxTokens;
132
154
  if (currentTokens < 1) {
133
155
  return false;
134
156
  }
@@ -173,7 +195,9 @@ export class TokenBucketTracker {
173
195
  drain(accountKey, quotaKey, drainAmount = 10) {
174
196
  const key = this.getKey(accountKey, quotaKey);
175
197
  const entry = this.buckets.get(key);
176
- const currentTokens = entry ? this.refillTokens(entry) : this.config.maxTokens;
198
+ const currentTokens = entry
199
+ ? this.refillTokens(entry)
200
+ : this.config.maxTokens;
177
201
  this.buckets.set(key, {
178
202
  tokens: Math.max(0, currentTokens - drainAmount),
179
203
  lastRefill: Date.now(),
@@ -187,6 +211,19 @@ export class TokenBucketTracker {
187
211
  clear() {
188
212
  this.buckets.clear();
189
213
  }
214
+ clearNumericKeysAtOrAbove(startIndex) {
215
+ for (const key of this.buckets.keys()) {
216
+ try {
217
+ const [accountKey] = JSON.parse(key);
218
+ if (/^\d+$/.test(accountKey) && Number(accountKey) >= startIndex) {
219
+ this.buckets.delete(key);
220
+ }
221
+ }
222
+ catch {
223
+ // Ignore malformed tracker keys.
224
+ }
225
+ }
226
+ }
190
227
  }
191
228
  export const DEFAULT_HYBRID_SELECTION_CONFIG = {
192
229
  healthWeight: 2,
@@ -199,9 +236,15 @@ export function selectHybridAccount(accountsOrParams, healthTracker, tokenTracke
199
236
  typeof accountsOrParams === "object"
200
237
  ? accountsOrParams
201
238
  : null;
202
- const resolvedAccounts = namedParams ? namedParams.accounts : accountsOrParams;
203
- const resolvedHealthTracker = namedParams ? namedParams.healthTracker : healthTracker;
204
- const resolvedTokenTracker = namedParams ? namedParams.tokenTracker : tokenTracker;
239
+ const resolvedAccounts = namedParams
240
+ ? namedParams.accounts
241
+ : accountsOrParams;
242
+ const resolvedHealthTracker = namedParams
243
+ ? namedParams.healthTracker
244
+ : healthTracker;
245
+ const resolvedTokenTracker = namedParams
246
+ ? namedParams.tokenTracker
247
+ : tokenTracker;
205
248
  const resolvedQuotaKey = namedParams ? namedParams.quotaKey : quotaKey;
206
249
  const resolvedConfig = namedParams ? (namedParams.config ?? {}) : config;
207
250
  const resolvedOptions = namedParams ? (namedParams.options ?? {}) : options;
@@ -213,18 +256,15 @@ export function selectHybridAccount(accountsOrParams, healthTracker, tokenTracke
213
256
  }
214
257
  const cfg = { ...DEFAULT_HYBRID_SELECTION_CONFIG, ...resolvedConfig };
215
258
  const available = resolvedAccounts.filter((a) => a.isAvailable);
259
+ // Contract: if NO account is currently available (all cooling down, rate-limited,
260
+ // circuit-open, or otherwise blocked) the selector returns null rather than
261
+ // handing back a known-unavailable candidate via an LRU fallback. Returning an
262
+ // unavailable account caused avoidable churn when the fetch loop trusted the
263
+ // result without re-validating (AUDIT-H2 / D-01). The caller is now responsible
264
+ // for surfacing the "no available accounts" condition (pool-wide cooldown,
265
+ // fast-fail, etc.) instead of retrying through a blocked candidate.
216
266
  if (available.length === 0) {
217
- if (resolvedAccounts.length === 0)
218
- return null;
219
- let leastRecentlyUsed = null;
220
- let oldestTime = Infinity;
221
- for (const account of resolvedAccounts) {
222
- if (account.lastUsed < oldestTime) {
223
- oldestTime = account.lastUsed;
224
- leastRecentlyUsed = account;
225
- }
226
- }
227
- return leastRecentlyUsed;
267
+ return null;
228
268
  }
229
269
  // istanbul ignore next -- defensive: available[0] always exists when length === 1
230
270
  if (available.length === 1)
@@ -234,23 +274,28 @@ export function selectHybridAccount(accountsOrParams, healthTracker, tokenTracke
234
274
  let bestScore = -Infinity;
235
275
  // PID offset: distribute account selection across parallel processes
236
276
  // Each process gets a small deterministic bonus based on its PID
237
- const pidBonus = resolvedOptions.pidOffsetEnabled ? (process.pid % 100) * 0.01 : 0;
277
+ const pidBonus = resolvedOptions.pidOffsetEnabled
278
+ ? (process.pid % 100) * 0.01
279
+ : 0;
238
280
  for (const account of available) {
239
281
  const trackerKey = account.trackerKey ?? account.index;
240
282
  const health = resolvedHealthTracker.getScore(trackerKey, resolvedQuotaKey);
241
283
  const tokens = resolvedTokenTracker.getTokens(trackerKey, resolvedQuotaKey);
242
284
  const hoursSinceUsed = (now - account.lastUsed) / (1000 * 60 * 60);
243
285
  const capabilityBoost = typeof resolvedOptions.scoreBoostByAccount?.[account.index] === "number"
244
- ? resolvedOptions.scoreBoostByAccount[account.index] ?? 0
286
+ ? (resolvedOptions.scoreBoostByAccount[account.index] ?? 0)
287
+ : 0;
288
+ const safeCapabilityBoost = Number.isFinite(capabilityBoost)
289
+ ? capabilityBoost
245
290
  : 0;
246
- const safeCapabilityBoost = Number.isFinite(capabilityBoost) ? capabilityBoost : 0;
247
291
  let score = health * cfg.healthWeight +
248
292
  tokens * cfg.tokenWeight +
249
293
  hoursSinceUsed * cfg.freshnessWeight +
250
294
  safeCapabilityBoost;
251
295
  // PID-based offset distributes selection across parallel agents
252
296
  if (resolvedOptions.pidOffsetEnabled) {
253
- score += ((account.index * 0.131 + pidBonus) % 1) * cfg.freshnessWeight * 0.1;
297
+ score +=
298
+ ((account.index * 0.131 + pidBonus) % 1) * cfg.freshnessWeight * 0.1;
254
299
  }
255
300
  if (score > bestScore) {
256
301
  bestScore = score;
@@ -271,6 +316,99 @@ export function selectHybridAccount(accountsOrParams, healthTracker, tokenTracke
271
316
  }
272
317
  return bestAccount;
273
318
  }
319
+ /**
320
+ * Non-mutating variant of {@link selectHybridAccount} that returns the winning
321
+ * account alongside a per-candidate score breakdown.
322
+ *
323
+ * The scoring logic mirrors `selectHybridAccount` exactly (same weights, same
324
+ * availability gating, same PID offset behaviour, same capability boost), so
325
+ * this function can be used by diagnostic commands (e.g. `codex auth
326
+ * why-selected --now`) without drifting from production selection. Candidates
327
+ * are returned sorted by descending score.
328
+ *
329
+ * No mutation of trackers or account state occurs; this is purely a read.
330
+ */
331
+ export function selectHybridAccountTraced(params) {
332
+ const cfg = { ...DEFAULT_HYBRID_SELECTION_CONFIG, ...(params.config ?? {}) };
333
+ const options = params.options ?? {};
334
+ const quotaKey = params.quotaKey;
335
+ const now = Date.now();
336
+ const pidBonus = options.pidOffsetEnabled ? (process.pid % 100) * 0.01 : 0;
337
+ const accounts = Array.isArray(params.accounts) ? params.accounts : [];
338
+ const availableAccounts = accounts.filter((account) => account.isAvailable);
339
+ const candidates = accounts.map((account) => {
340
+ const trackerKey = account.trackerKey ?? account.index;
341
+ const health = params.healthTracker.getScore(trackerKey, quotaKey);
342
+ const tokens = params.tokenTracker.getTokens(trackerKey, quotaKey);
343
+ const hoursSinceUsed = (now - account.lastUsed) / (1000 * 60 * 60);
344
+ const rawCapabilityBoost = typeof options.scoreBoostByAccount?.[account.index] === "number"
345
+ ? (options.scoreBoostByAccount[account.index] ?? 0)
346
+ : 0;
347
+ const capabilityBoost = Number.isFinite(rawCapabilityBoost)
348
+ ? rawCapabilityBoost
349
+ : 0;
350
+ let score = health * cfg.healthWeight +
351
+ tokens * cfg.tokenWeight +
352
+ hoursSinceUsed * cfg.freshnessWeight +
353
+ capabilityBoost;
354
+ if (options.pidOffsetEnabled) {
355
+ score +=
356
+ ((account.index * 0.131 + pidBonus) % 1) * cfg.freshnessWeight * 0.1;
357
+ }
358
+ return {
359
+ index: account.index,
360
+ trackerKey,
361
+ isAvailable: account.isAvailable,
362
+ lastUsed: account.lastUsed,
363
+ health,
364
+ tokens,
365
+ hoursSinceUsed,
366
+ capabilityBoost,
367
+ pidBonus: options.pidOffsetEnabled ? pidBonus : 0,
368
+ score,
369
+ reason: account.isAvailable
370
+ ? undefined
371
+ : "unavailable (rate-limited, cooling down, or circuit open)",
372
+ };
373
+ });
374
+ candidates.sort((a, b) => b.score - a.score);
375
+ let selected = null;
376
+ let selectionReason = "no accounts provided";
377
+ if (accounts.length === 0) {
378
+ selectionReason = "no accounts configured";
379
+ }
380
+ else if (availableAccounts.length === 0) {
381
+ // AUDIT-H2 / D-01 contract: when no account is available, selection
382
+ // returns null rather than a blocked LRU candidate. The trace variant
383
+ // mirrors the production contract so why-selected diagnostics match
384
+ // actual selection behaviour.
385
+ selected = null;
386
+ selectionReason =
387
+ "all accounts unavailable (rate-limited, cooling down, or circuit open)";
388
+ }
389
+ else if (availableAccounts.length === 1) {
390
+ selected = availableAccounts[0] ?? null;
391
+ selectionReason = "single available account";
392
+ }
393
+ else {
394
+ const bestCandidate = candidates.find((candidate) => candidate.isAvailable);
395
+ if (bestCandidate) {
396
+ selected =
397
+ accounts.find((account) => account.index === bestCandidate.index) ??
398
+ null;
399
+ selectionReason = `highest hybrid score (${bestCandidate.score.toFixed(2)}) across ${availableAccounts.length} available account(s)`;
400
+ }
401
+ }
402
+ return {
403
+ selected,
404
+ selectedIndex: selected?.index ?? null,
405
+ selectionReason,
406
+ candidates,
407
+ config: cfg,
408
+ quotaKey,
409
+ availableCount: availableAccounts.length,
410
+ };
411
+ }
274
412
  // ============================================================================
275
413
  // Utility Functions
276
414
  // ============================================================================
@@ -1 +1 @@
1
- {"version":3,"file":"rotation.js","sourceRoot":"","sources":["../../lib/rotation.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"rotation.js","sourceRoot":"","sources":["../../lib/rotation.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,107 @@
1
+ /**
2
+ * Routing Mutex Module (PR-N / R4)
3
+ *
4
+ * Provides an in-memory, promise-chain-based async mutex used to serialize
5
+ * cursor mutation sites in the account rotation pipeline (see `lib/accounts.ts`).
6
+ *
7
+ * Concurrency background:
8
+ * The legacy rotation path reads `activeIndex` / `activeIndexByFamily`, picks
9
+ * a candidate, and then writes the cursor back without any lock. Two
10
+ * simultaneous requests can therefore (a) observe the same `activeIndex`,
11
+ * (b) both decide to rotate to the same candidate, and (c) race on
12
+ * `markSwitched` / `markAccountCoolingDown`. The resulting "lost update"
13
+ * window allows the same account to be chosen twice inside a single
14
+ * cooldown window.
15
+ *
16
+ * Rollout:
17
+ * Gated behind `PluginConfig.routingMutex` = `"legacy" | "enabled"`.
18
+ * Default is `"legacy"` for one full release cycle so existing users see
19
+ * zero behaviour change. When flipped to `"enabled"` the mutex serializes
20
+ * every cursor-mutation critical section through `runExclusive`.
21
+ *
22
+ * Non-goals:
23
+ * - No new runtime dependency. This file intentionally avoids importing
24
+ * `async-mutex` or similar npm packages.
25
+ * - No cross-process coordination. The pool manager is a single in-process
26
+ * singleton; OS-level mutexes are out of scope for this PR.
27
+ */
28
+ export type RoutingMutexMode = "enabled" | "legacy";
29
+ /**
30
+ * Single selection decision emitted by the rotation pipeline.
31
+ *
32
+ * Threaded through the fetch loop so downstream consumers (observability,
33
+ * why-selected trace, failure-policy telemetry) can inspect *why* a
34
+ * particular account was picked for a given request without recomputing
35
+ * scores.
36
+ *
37
+ * All fields are optional except the core identity + reason + timestamp so
38
+ * the record can be produced from both the legacy fast-path and the full
39
+ * scoring path.
40
+ */
41
+ export interface SelectionRecord {
42
+ /** Index of the selected account inside the managed pool. */
43
+ accountIndex: number;
44
+ /** Stable account identifier (accountId, email, or runtime key). */
45
+ accountId: string;
46
+ /** Coarse-grained classification of why this account was selected. */
47
+ reason: "initial" | "rotation" | "rate-limit" | "best" | "restore";
48
+ /** Wall-clock timestamp (ms since epoch) when the decision was made. */
49
+ timestamp: number;
50
+ /** Quota tracker key (e.g. `"codex"` or `"codex:gpt-5-codex"`) when scoped. */
51
+ trackerKeyQuota?: string;
52
+ /** Health score (0-100) used when ranking candidates. */
53
+ health?: number;
54
+ /** Token bucket remaining count used when ranking candidates. */
55
+ tokens?: number;
56
+ /** Final hybrid score used during selection. */
57
+ score?: number;
58
+ }
59
+ /**
60
+ * Minimal async mutex contract. Intentionally small so we can swap in a
61
+ * different implementation (e.g. with debounced fairness) without breaking
62
+ * callers.
63
+ */
64
+ export interface AsyncMutex {
65
+ /**
66
+ * Run `fn` while holding the mutex. The mutex is released automatically
67
+ * whether `fn` resolves or throws. Errors are propagated to the caller.
68
+ */
69
+ runExclusive<T>(fn: () => Promise<T> | T): Promise<T>;
70
+ /** Returns true while some critical section is executing. */
71
+ isLocked(): boolean;
72
+ /** Number of queued (not yet running) tasks. */
73
+ queueLength(): number;
74
+ }
75
+ /**
76
+ * Create a promise-chain async mutex.
77
+ *
78
+ * Implementation notes:
79
+ * - Serialization is achieved by chaining each new task onto the previous
80
+ * task's completion settle. Using `.catch(() => {})` on the chain head
81
+ * prevents a rejected task from poisoning subsequent waiters.
82
+ * - `runExclusive` always returns a fresh promise that resolves/rejects
83
+ * with the task's own result.
84
+ * - `isLocked` and `queueLength` are bookkeeping counters; they do not
85
+ * influence execution order.
86
+ */
87
+ export declare function createAsyncMutex(): AsyncMutex;
88
+ /**
89
+ * Singleton accessor for the rotation critical-section mutex.
90
+ *
91
+ * Callers in `lib/accounts.ts` use this when the plugin config flag
92
+ * `routingMutex === "enabled"`; the legacy mode bypasses the mutex entirely
93
+ * (no wait, no allocation).
94
+ */
95
+ export declare function getRoutingMutex(): AsyncMutex;
96
+ /**
97
+ * Test-only reset so each vitest file starts from a clean mutex state.
98
+ * Not exported through `lib/index.ts`.
99
+ */
100
+ export declare function __resetRoutingMutexForTests(): void;
101
+ /**
102
+ * Run `fn` under the routing mutex when `mode === "enabled"`, otherwise run
103
+ * it inline. Hot-path helper used by account-pool mutation sites so the
104
+ * flag check stays O(1) per call.
105
+ */
106
+ export declare function withRoutingMutex<T>(mode: RoutingMutexMode, fn: () => Promise<T> | T): Promise<T>;
107
+ //# sourceMappingURL=routing-mutex.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routing-mutex.d.ts","sourceRoot":"","sources":["../../lib/routing-mutex.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,MAAM,MAAM,gBAAgB,GAAG,SAAS,GAAG,QAAQ,CAAC;AAEpD;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,eAAe;IAC/B,6DAA6D;IAC7D,YAAY,EAAE,MAAM,CAAC;IACrB,oEAAoE;IACpE,SAAS,EAAE,MAAM,CAAC;IAClB,sEAAsE;IACtE,MAAM,EAAE,SAAS,GAAG,UAAU,GAAG,YAAY,GAAG,MAAM,GAAG,SAAS,CAAC;IACnE,wEAAwE;IACxE,SAAS,EAAE,MAAM,CAAC;IAClB,+EAA+E;IAC/E,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,yDAAyD;IACzD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,iEAAiE;IACjE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,gDAAgD;IAChD,KAAK,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;;GAIG;AACH,MAAM,WAAW,UAAU;IAC1B;;;OAGG;IACH,YAAY,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IACtD,6DAA6D;IAC7D,QAAQ,IAAI,OAAO,CAAC;IACpB,gDAAgD;IAChD,WAAW,IAAI,MAAM,CAAC;CACtB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,gBAAgB,IAAI,UAAU,CAgC7C;AAID;;;;;;GAMG;AACH,wBAAgB,eAAe,IAAI,UAAU,CAK5C;AAED;;;GAGG;AACH,wBAAgB,2BAA2B,IAAI,IAAI,CAElD;AAED;;;;GAIG;AACH,wBAAsB,gBAAgB,CAAC,CAAC,EACvC,IAAI,EAAE,gBAAgB,EACtB,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,GACtB,OAAO,CAAC,CAAC,CAAC,CAKZ"}
@@ -0,0 +1,104 @@
1
+ /**
2
+ * Routing Mutex Module (PR-N / R4)
3
+ *
4
+ * Provides an in-memory, promise-chain-based async mutex used to serialize
5
+ * cursor mutation sites in the account rotation pipeline (see `lib/accounts.ts`).
6
+ *
7
+ * Concurrency background:
8
+ * The legacy rotation path reads `activeIndex` / `activeIndexByFamily`, picks
9
+ * a candidate, and then writes the cursor back without any lock. Two
10
+ * simultaneous requests can therefore (a) observe the same `activeIndex`,
11
+ * (b) both decide to rotate to the same candidate, and (c) race on
12
+ * `markSwitched` / `markAccountCoolingDown`. The resulting "lost update"
13
+ * window allows the same account to be chosen twice inside a single
14
+ * cooldown window.
15
+ *
16
+ * Rollout:
17
+ * Gated behind `PluginConfig.routingMutex` = `"legacy" | "enabled"`.
18
+ * Default is `"legacy"` for one full release cycle so existing users see
19
+ * zero behaviour change. When flipped to `"enabled"` the mutex serializes
20
+ * every cursor-mutation critical section through `runExclusive`.
21
+ *
22
+ * Non-goals:
23
+ * - No new runtime dependency. This file intentionally avoids importing
24
+ * `async-mutex` or similar npm packages.
25
+ * - No cross-process coordination. The pool manager is a single in-process
26
+ * singleton; OS-level mutexes are out of scope for this PR.
27
+ */
28
+ /**
29
+ * Create a promise-chain async mutex.
30
+ *
31
+ * Implementation notes:
32
+ * - Serialization is achieved by chaining each new task onto the previous
33
+ * task's completion settle. Using `.catch(() => {})` on the chain head
34
+ * prevents a rejected task from poisoning subsequent waiters.
35
+ * - `runExclusive` always returns a fresh promise that resolves/rejects
36
+ * with the task's own result.
37
+ * - `isLocked` and `queueLength` are bookkeeping counters; they do not
38
+ * influence execution order.
39
+ */
40
+ export function createAsyncMutex() {
41
+ let tail = Promise.resolve();
42
+ let running = 0;
43
+ let queued = 0;
44
+ const runExclusive = async (fn) => {
45
+ queued += 1;
46
+ const previous = tail;
47
+ let release;
48
+ const next = new Promise((resolve) => {
49
+ release = resolve;
50
+ });
51
+ tail = next;
52
+ try {
53
+ await previous.catch(() => {
54
+ // Swallow upstream failures; each task reports its own error
55
+ // via the returned promise. This keeps the chain alive.
56
+ });
57
+ queued = Math.max(0, queued - 1);
58
+ running += 1;
59
+ return await fn();
60
+ }
61
+ finally {
62
+ running = Math.max(0, running - 1);
63
+ release?.();
64
+ }
65
+ };
66
+ return {
67
+ runExclusive,
68
+ isLocked: () => running > 0,
69
+ queueLength: () => queued,
70
+ };
71
+ }
72
+ let routingMutexInstance = null;
73
+ /**
74
+ * Singleton accessor for the rotation critical-section mutex.
75
+ *
76
+ * Callers in `lib/accounts.ts` use this when the plugin config flag
77
+ * `routingMutex === "enabled"`; the legacy mode bypasses the mutex entirely
78
+ * (no wait, no allocation).
79
+ */
80
+ export function getRoutingMutex() {
81
+ if (!routingMutexInstance) {
82
+ routingMutexInstance = createAsyncMutex();
83
+ }
84
+ return routingMutexInstance;
85
+ }
86
+ /**
87
+ * Test-only reset so each vitest file starts from a clean mutex state.
88
+ * Not exported through `lib/index.ts`.
89
+ */
90
+ export function __resetRoutingMutexForTests() {
91
+ routingMutexInstance = null;
92
+ }
93
+ /**
94
+ * Run `fn` under the routing mutex when `mode === "enabled"`, otherwise run
95
+ * it inline. Hot-path helper used by account-pool mutation sites so the
96
+ * flag check stays O(1) per call.
97
+ */
98
+ export async function withRoutingMutex(mode, fn) {
99
+ if (mode === "enabled") {
100
+ return getRoutingMutex().runExclusive(fn);
101
+ }
102
+ return await fn();
103
+ }
104
+ //# sourceMappingURL=routing-mutex.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routing-mutex.js","sourceRoot":"","sources":["../../lib/routing-mutex.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAoDH;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,gBAAgB;IAC/B,IAAI,IAAI,GAAkB,OAAO,CAAC,OAAO,EAAE,CAAC;IAC5C,IAAI,OAAO,GAAG,CAAC,CAAC;IAChB,IAAI,MAAM,GAAG,CAAC,CAAC;IAEf,MAAM,YAAY,GAAG,KAAK,EAAK,EAAwB,EAAc,EAAE;QACtE,MAAM,IAAI,CAAC,CAAC;QACZ,MAAM,QAAQ,GAAG,IAAI,CAAC;QACtB,IAAI,OAAiC,CAAC;QACtC,MAAM,IAAI,GAAG,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;YAC1C,OAAO,GAAG,OAAO,CAAC;QACnB,CAAC,CAAC,CAAC;QACH,IAAI,GAAG,IAAI,CAAC;QACZ,IAAI,CAAC;YACJ,MAAM,QAAQ,CAAC,KAAK,CAAC,GAAG,EAAE;gBACzB,6DAA6D;gBAC7D,wDAAwD;YACzD,CAAC,CAAC,CAAC;YACH,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,GAAG,CAAC,CAAC,CAAC;YACjC,OAAO,IAAI,CAAC,CAAC;YACb,OAAO,MAAM,EAAE,EAAE,CAAC;QACnB,CAAC;gBAAS,CAAC;YACV,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,GAAG,CAAC,CAAC,CAAC;YACnC,OAAO,EAAE,EAAE,CAAC;QACb,CAAC;IACF,CAAC,CAAC;IAEF,OAAO;QACN,YAAY;QACZ,QAAQ,EAAE,GAAG,EAAE,CAAC,OAAO,GAAG,CAAC;QAC3B,WAAW,EAAE,GAAG,EAAE,CAAC,MAAM;KACzB,CAAC;AACH,CAAC;AAED,IAAI,oBAAoB,GAAsB,IAAI,CAAC;AAEnD;;;;;;GAMG;AACH,MAAM,UAAU,eAAe;IAC9B,IAAI,CAAC,oBAAoB,EAAE,CAAC;QAC3B,oBAAoB,GAAG,gBAAgB,EAAE,CAAC;IAC3C,CAAC;IACD,OAAO,oBAAoB,CAAC;AAC7B,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,2BAA2B;IAC1C,oBAAoB,GAAG,IAAI,CAAC;AAC7B,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACrC,IAAsB,EACtB,EAAwB;IAExB,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;QACxB,OAAO,eAAe,EAAE,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC;IAC3C,CAAC;IACD,OAAO,MAAM,EAAE,EAAE,CAAC;AACnB,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"storage-scope.d.ts","sourceRoot":"","sources":["../../../lib/runtime/storage-scope.ts"],"names":[],"mappings":"AAAA,wBAAgB,kCAAkC,CACjD,YAAY,EAAE,UAAU,CAAC,cAAc,cAAc,EAAE,gBAAgB,CAAC,EACxE,IAAI,EAAE;IACL,qBAAqB,EAAE,CACtB,MAAM,EAAE,UAAU,CAAC,cAAc,cAAc,EAAE,gBAAgB,CAAC,KAC9D,OAAO,CAAC;IACb,uBAAuB,EAAE,CACxB,MAAM,EAAE,UAAU,CAAC,cAAc,cAAc,EAAE,gBAAgB,CAAC,KAC9D,OAAO,CAAC;IACb,uBAAuB,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;IACpD,qBAAqB,EAAE,MAAM,OAAO,CAAC;IACrC,eAAe,EAAE,MAAM,OAAO,CAAC;IAC/B,eAAe,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;IAC1C,OAAO,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;IAC9C,GAAG,EAAE,MAAM,MAAM,CAAC;CAClB,GACC,IAAI,CAeN"}
1
+ {"version":3,"file":"storage-scope.d.ts","sourceRoot":"","sources":["../../../lib/runtime/storage-scope.ts"],"names":[],"mappings":"AAAA,wBAAgB,kCAAkC,CACjD,YAAY,EAAE,UAAU,CAAC,cAAc,cAAc,EAAE,gBAAgB,CAAC,EACxE,IAAI,EAAE;IACL,qBAAqB,EAAE,CACtB,MAAM,EAAE,UAAU,CAAC,cAAc,cAAc,EAAE,gBAAgB,CAAC,KAC9D,OAAO,CAAC;IACb,uBAAuB,EAAE,CACxB,MAAM,EAAE,UAAU,CAAC,cAAc,cAAc,EAAE,gBAAgB,CAAC,KAC9D,OAAO,CAAC;IACb,uBAAuB,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;IACpD,qBAAqB,EAAE,MAAM,OAAO,CAAC;IACrC,eAAe,EAAE,MAAM,OAAO,CAAC;IAC/B,eAAe,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;IAC1C,OAAO,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;IAC9C,GAAG,EAAE,MAAM,MAAM,CAAC;CAClB,GACC,IAAI,CA+BN"}
@@ -4,7 +4,23 @@ export function applyAccountStorageScopeFromConfig(pluginConfig, deps) {
4
4
  if (deps.isCodexCliSyncEnabled()) {
5
5
  if (perProjectAccounts && !deps.getWarningShown()) {
6
6
  deps.setWarningShown(true);
7
- deps.logWarn(`[${deps.pluginName}] CODEX_AUTH_PER_PROJECT_ACCOUNTS is ignored while Codex CLI sync is enabled. Using global account storage.`);
7
+ // AUDIT-M09 / D-06: this is a hard config conflict that silently
8
+ // collapses per-project isolation to global storage. Surface it as
9
+ // a loud, actionable warning with explicit remediation so users
10
+ // who picked per-project accounts on purpose do NOT learn about
11
+ // the collapse via a post-hoc audit. We keep the warn-once gate
12
+ // so the log is not repeated every request — the signal is the
13
+ // fact that the user sees it, not its repetition.
14
+ deps.logWarn(`[${deps.pluginName}] Config conflict: perProjectAccounts = true ` +
15
+ `is ignored while Codex CLI sync is enabled, because Codex CLI ` +
16
+ `maintains a single shared account set. All multi-auth accounts ` +
17
+ `are collapsed to the GLOBAL pool (credentials are NOT isolated ` +
18
+ `per project/worktree). ` +
19
+ `To restore per-project isolation, either (a) disable Codex CLI ` +
20
+ `sync via 'codex auth config set codexCliSync false', or ` +
21
+ `(b) disable perProjectAccounts via ` +
22
+ `'codex auth config set perProjectAccounts false' (acknowledges ` +
23
+ `the global scope). This warning is emitted once per process.`);
8
24
  }
9
25
  deps.setStoragePath(null);
10
26
  return;
@@ -1 +1 @@
1
- {"version":3,"file":"storage-scope.js","sourceRoot":"","sources":["../../../lib/runtime/storage-scope.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,kCAAkC,CACjD,YAAwE,EACxE,IAeC;IAED,MAAM,kBAAkB,GAAG,IAAI,CAAC,qBAAqB,CAAC,YAAY,CAAC,CAAC;IACpE,IAAI,CAAC,uBAAuB,CAAC,IAAI,CAAC,uBAAuB,CAAC,YAAY,CAAC,CAAC,CAAC;IACzE,IAAI,IAAI,CAAC,qBAAqB,EAAE,EAAE,CAAC;QAClC,IAAI,kBAAkB,IAAI,CAAC,IAAI,CAAC,eAAe,EAAE,EAAE,CAAC;YACnD,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC;YAC3B,IAAI,CAAC,OAAO,CACX,IAAI,IAAI,CAAC,UAAU,6GAA6G,CAChI,CAAC;QACH,CAAC;QACD,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC;QAC1B,OAAO;IACR,CAAC;IAED,IAAI,CAAC,cAAc,CAAC,kBAAkB,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;AAC7D,CAAC"}
1
+ {"version":3,"file":"storage-scope.js","sourceRoot":"","sources":["../../../lib/runtime/storage-scope.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,kCAAkC,CACjD,YAAwE,EACxE,IAeC;IAED,MAAM,kBAAkB,GAAG,IAAI,CAAC,qBAAqB,CAAC,YAAY,CAAC,CAAC;IACpE,IAAI,CAAC,uBAAuB,CAAC,IAAI,CAAC,uBAAuB,CAAC,YAAY,CAAC,CAAC,CAAC;IACzE,IAAI,IAAI,CAAC,qBAAqB,EAAE,EAAE,CAAC;QAClC,IAAI,kBAAkB,IAAI,CAAC,IAAI,CAAC,eAAe,EAAE,EAAE,CAAC;YACnD,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC;YAC3B,iEAAiE;YACjE,mEAAmE;YACnE,gEAAgE;YAChE,gEAAgE;YAChE,gEAAgE;YAChE,+DAA+D;YAC/D,kDAAkD;YAClD,IAAI,CAAC,OAAO,CACX,IAAI,IAAI,CAAC,UAAU,+CAA+C;gBACjE,gEAAgE;gBAChE,iEAAiE;gBACjE,iEAAiE;gBACjE,yBAAyB;gBACzB,iEAAiE;gBACjE,0DAA0D;gBAC1D,qCAAqC;gBACrC,iEAAiE;gBACjE,8DAA8D,CAC/D,CAAC;QACH,CAAC;QACD,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC;QAC1B,OAAO;IACR,CAAC;IAED,IAAI,CAAC,cAAc,CAAC,kBAAkB,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;AAC7D,CAAC"}