oc-codex-multi-auth 6.14.3 → 6.15.0

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.
@@ -0,0 +1,59 @@
1
+ import { execFile } from "node:child_process";
2
+ import { logDebug, logWarn } from "./logger.js";
3
+ const DELIVERY_TIMEOUT_MS = 10_000;
4
+ const MACOS_NOTIFICATION_SCRIPT = `
5
+ function run(argv) {
6
+ const app = Application.currentApplication();
7
+ app.includeStandardAdditions = true;
8
+ app.displayNotification(argv[1], { withTitle: argv[0] });
9
+ }
10
+ `.trim();
11
+ const executeFile = (file, args, options, callback) => {
12
+ return execFile(file, args, options, (error) => callback(error));
13
+ };
14
+ function getErrorCode(error) {
15
+ if (typeof error !== "object" || error === null)
16
+ return "unknown";
17
+ const code = error.code;
18
+ return typeof code === "string" || typeof code === "number" ? String(code) : "unknown";
19
+ }
20
+ export function isDesktopNotificationSupported(platform = process.platform) {
21
+ return platform === "darwin";
22
+ }
23
+ export function createDesktopNotifier(options) {
24
+ const platform = options?.platform ?? process.platform;
25
+ const run = options?.executeFile ?? executeFile;
26
+ let warningLogged = false;
27
+ const warnOnce = (message) => {
28
+ if (warningLogged)
29
+ return;
30
+ warningLogged = true;
31
+ logWarn(message);
32
+ };
33
+ return async (title, message) => {
34
+ if (!isDesktopNotificationSupported(platform)) {
35
+ warnOnce("Desktop quota notifications are supported only on macOS.");
36
+ return false;
37
+ }
38
+ return await new Promise((resolve) => {
39
+ try {
40
+ run("/usr/bin/osascript", ["-l", "JavaScript", "-e", MACOS_NOTIFICATION_SCRIPT, title, message], { timeout: DELIVERY_TIMEOUT_MS, windowsHide: true }, (error) => {
41
+ if (!error) {
42
+ resolve(true);
43
+ return;
44
+ }
45
+ logDebug(`Desktop quota notification failed with code ${getErrorCode(error)}`);
46
+ warnOnce("Desktop quota notification delivery failed on macOS.");
47
+ resolve(false);
48
+ });
49
+ }
50
+ catch (error) {
51
+ logDebug(`Desktop quota notification threw with code ${getErrorCode(error)}`);
52
+ warnOnce("Desktop quota notification delivery failed on macOS.");
53
+ resolve(false);
54
+ }
55
+ });
56
+ };
57
+ }
58
+ export const sendDesktopNotification = createDesktopNotifier();
59
+ //# sourceMappingURL=desktop-notifications.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"desktop-notifications.js","sourceRoot":"","sources":["../../lib/desktop-notifications.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAE9C,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAEhD,MAAM,mBAAmB,GAAG,MAAM,CAAC;AACnC,MAAM,yBAAyB,GAAG;;;;;;CAMjC,CAAC,IAAI,EAAE,CAAC;AAYT,MAAM,WAAW,GAAgB,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE;IAClE,OAAO,QAAQ,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC;AAClE,CAAC,CAAC;AAEF,SAAS,YAAY,CAAC,KAAc;IACnC,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,SAAS,CAAC;IAClE,MAAM,IAAI,GAAI,KAAiC,CAAC,IAAI,CAAC;IACrD,OAAO,OAAO,IAAI,KAAK,QAAQ,IAAI,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;AACxF,CAAC;AAED,MAAM,UAAU,8BAA8B,CAC7C,WAA4B,OAAO,CAAC,QAAQ;IAE5C,OAAO,QAAQ,KAAK,QAAQ,CAAC;AAC9B,CAAC;AAED,MAAM,UAAU,qBAAqB,CAAC,OAGrC;IACA,MAAM,QAAQ,GAAG,OAAO,EAAE,QAAQ,IAAI,OAAO,CAAC,QAAQ,CAAC;IACvD,MAAM,GAAG,GAAG,OAAO,EAAE,WAAW,IAAI,WAAW,CAAC;IAChD,IAAI,aAAa,GAAG,KAAK,CAAC;IAE1B,MAAM,QAAQ,GAAG,CAAC,OAAe,EAAQ,EAAE;QAC1C,IAAI,aAAa;YAAE,OAAO;QAC1B,aAAa,GAAG,IAAI,CAAC;QACrB,OAAO,CAAC,OAAO,CAAC,CAAC;IAClB,CAAC,CAAC;IAEF,OAAO,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE;QAC/B,IAAI,CAAC,8BAA8B,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC/C,QAAQ,CAAC,0DAA0D,CAAC,CAAC;YACrE,OAAO,KAAK,CAAC;QACd,CAAC;QAED,OAAO,MAAM,IAAI,OAAO,CAAU,CAAC,OAAO,EAAE,EAAE;YAC7C,IAAI,CAAC;gBACJ,GAAG,CACF,oBAAoB,EACpB,CAAC,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,yBAAyB,EAAE,KAAK,EAAE,OAAO,CAAC,EACrE,EAAE,OAAO,EAAE,mBAAmB,EAAE,WAAW,EAAE,IAAI,EAAE,EACnD,CAAC,KAAK,EAAE,EAAE;oBACT,IAAI,CAAC,KAAK,EAAE,CAAC;wBACZ,OAAO,CAAC,IAAI,CAAC,CAAC;wBACd,OAAO;oBACR,CAAC;oBACD,QAAQ,CAAC,+CAA+C,YAAY,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;oBAC/E,QAAQ,CAAC,sDAAsD,CAAC,CAAC;oBACjE,OAAO,CAAC,KAAK,CAAC,CAAC;gBAChB,CAAC,CACD,CAAC;YACH,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBAChB,QAAQ,CAAC,8CAA8C,YAAY,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;gBAC9E,QAAQ,CAAC,sDAAsD,CAAC,CAAC;gBACjE,OAAO,CAAC,KAAK,CAAC,CAAC;YAChB,CAAC;QACF,CAAC,CAAC,CAAC;IACJ,CAAC,CAAC;AACH,CAAC;AAED,MAAM,CAAC,MAAM,uBAAuB,GAAG,qBAAqB,EAAE,CAAC"}
@@ -0,0 +1,25 @@
1
+ export interface QuotaWindowNotificationState {
2
+ /**
3
+ * The remaining-percent reading this window was last seen at. It is the
4
+ * only per-window state: which threshold is "armed" is derived from it, so
5
+ * storing that separately would let a corrupt extra field invalidate an
6
+ * otherwise usable file.
7
+ */
8
+ lastPercent?: number;
9
+ }
10
+ export interface QuotaNotificationState {
11
+ fiveHour: QuotaWindowNotificationState;
12
+ weekly: QuotaWindowNotificationState;
13
+ lastDeliveredAt?: number;
14
+ updatedAt: number;
15
+ }
16
+ export declare function getQuotaNotificationStatePath(activeProjectStoragePath: string): string;
17
+ export declare function readQuotaNotificationState(statePath: string): Promise<QuotaNotificationState | undefined>;
18
+ export declare function updateQuotaNotificationState<T>(statePath: string, update: (state: QuotaNotificationState | undefined) => Promise<{
19
+ state: QuotaNotificationState;
20
+ result: T;
21
+ }> | {
22
+ state: QuotaNotificationState;
23
+ result: T;
24
+ }): Promise<T>;
25
+ //# sourceMappingURL=quota-notification-state.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"quota-notification-state.d.ts","sourceRoot":"","sources":["../../lib/quota-notification-state.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,4BAA4B;IAC5C;;;;;OAKG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,sBAAsB;IACtC,QAAQ,EAAE,4BAA4B,CAAC;IACvC,MAAM,EAAE,4BAA4B,CAAC;IACrC,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,SAAS,EAAE,MAAM,CAAC;CAClB;AAED,wBAAgB,6BAA6B,CAAC,wBAAwB,EAAE,MAAM,GAAG,MAAM,CAEtF;AAsCD,wBAAsB,0BAA0B,CAC/C,SAAS,EAAE,MAAM,GACf,OAAO,CAAC,sBAAsB,GAAG,SAAS,CAAC,CAS7C;AAYD,wBAAsB,4BAA4B,CAAC,CAAC,EACnD,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,CAAC,KAAK,EAAE,sBAAsB,GAAG,SAAS,KAAK,OAAO,CAAC;IAC9D,KAAK,EAAE,sBAAsB,CAAC;IAC9B,MAAM,EAAE,CAAC,CAAC;CACV,CAAC,GAAG;IACJ,KAAK,EAAE,sBAAsB,CAAC;IAC9B,MAAM,EAAE,CAAC,CAAC;CACV,GACC,OAAO,CAAC,CAAC,CAAC,CA8BZ"}
@@ -0,0 +1,94 @@
1
+ import { promises as fs } from "node:fs";
2
+ import { dirname, join } from "node:path";
3
+ import { lock } from "proper-lockfile";
4
+ import { renameWithWindowsRetry } from "./storage/atomic-write.js";
5
+ import { logDebug, logWarn } from "./logger.js";
6
+ export function getQuotaNotificationStatePath(activeProjectStoragePath) {
7
+ return join(dirname(activeProjectStoragePath), "oc-codex-multi-auth-quota-notifications.json");
8
+ }
9
+ function parseWindowState(value) {
10
+ if (typeof value !== "object" || value === null)
11
+ return undefined;
12
+ const record = value;
13
+ const lastPercent = record.lastPercent;
14
+ if (lastPercent !== undefined &&
15
+ (typeof lastPercent !== "number" ||
16
+ !Number.isFinite(lastPercent) ||
17
+ lastPercent < 0 ||
18
+ lastPercent > 100)) {
19
+ return undefined;
20
+ }
21
+ // Older files may carry an `activeThreshold` key. It is ignored rather than
22
+ // rejected so an upgrade does not discard a usable state file.
23
+ return { lastPercent };
24
+ }
25
+ function parseState(value) {
26
+ if (typeof value !== "object" || value === null)
27
+ return undefined;
28
+ const record = value;
29
+ const fiveHour = parseWindowState(record.fiveHour);
30
+ const weekly = parseWindowState(record.weekly);
31
+ const lastDeliveredAt = record.lastDeliveredAt;
32
+ if (!fiveHour || !weekly || typeof record.updatedAt !== "number" || !Number.isFinite(record.updatedAt)) {
33
+ return undefined;
34
+ }
35
+ if (lastDeliveredAt !== undefined &&
36
+ (typeof lastDeliveredAt !== "number" || !Number.isFinite(lastDeliveredAt) || lastDeliveredAt < 0)) {
37
+ return undefined;
38
+ }
39
+ return { fiveHour, weekly, lastDeliveredAt, updatedAt: record.updatedAt };
40
+ }
41
+ export async function readQuotaNotificationState(statePath) {
42
+ try {
43
+ return parseState(JSON.parse(await fs.readFile(statePath, "utf-8")));
44
+ }
45
+ catch (error) {
46
+ if (error.code !== "ENOENT") {
47
+ logWarn(`Failed to read quota notification state from ${statePath}: ${error.message}`);
48
+ }
49
+ return undefined;
50
+ }
51
+ }
52
+ async function writeState(statePath, state) {
53
+ const tempPath = `${statePath}.${process.pid}.${Date.now()}.tmp`;
54
+ try {
55
+ await fs.writeFile(tempPath, JSON.stringify(state, null, 2), { mode: 0o600 });
56
+ await renameWithWindowsRetry(tempPath, statePath);
57
+ }
58
+ finally {
59
+ await fs.rm(tempPath, { force: true }).catch(() => undefined);
60
+ }
61
+ }
62
+ export async function updateQuotaNotificationState(statePath, update) {
63
+ await fs.mkdir(dirname(statePath), { recursive: true, mode: 0o700 });
64
+ let compromised;
65
+ const release = await lock(statePath, {
66
+ realpath: false,
67
+ stale: 10_000,
68
+ update: 2_000,
69
+ retries: {
70
+ retries: 5,
71
+ factor: 1.5,
72
+ minTimeout: 50,
73
+ maxTimeout: 500,
74
+ randomize: true,
75
+ },
76
+ onCompromised: (error) => {
77
+ compromised = error;
78
+ logDebug(`Quota notification state lock compromised: ${error.message}`);
79
+ },
80
+ });
81
+ try {
82
+ const next = await update(await readQuotaNotificationState(statePath));
83
+ if (compromised)
84
+ throw compromised;
85
+ await writeState(statePath, next.state);
86
+ return next.result;
87
+ }
88
+ finally {
89
+ await release().catch((error) => {
90
+ logDebug(`Failed to release quota notification state lock: ${error.message}`);
91
+ });
92
+ }
93
+ }
94
+ //# sourceMappingURL=quota-notification-state.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"quota-notification-state.js","sourceRoot":"","sources":["../../lib/quota-notification-state.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,64 @@
1
+ import { type QuotaNotificationsConfig } from "./config.js";
2
+ import { type CodexUsageSummary } from "./codex-usage.js";
3
+ import { type DesktopNotifier } from "./desktop-notifications.js";
4
+ import { type QuotaNotificationState, type QuotaWindowNotificationState } from "./quota-notification-state.js";
5
+ import { type AccountMetadataV3, type AccountStorageV3 } from "./storage.js";
6
+ export type QuotaWindowName = "fiveHour" | "weekly";
7
+ export interface AggregatedQuotaWindow {
8
+ remainingPercent?: number;
9
+ resetAtMs?: number;
10
+ }
11
+ export interface AggregatedQuotaUsage {
12
+ fiveHour: AggregatedQuotaWindow;
13
+ weekly: AggregatedQuotaWindow;
14
+ }
15
+ export interface QuotaThresholdCrossing {
16
+ window: QuotaWindowName;
17
+ threshold: number;
18
+ remainingPercent: number;
19
+ resetAtMs?: number;
20
+ }
21
+ export interface AccountQuotaSummary {
22
+ usage: CodexUsageSummary;
23
+ }
24
+ export interface QuotaMonitor {
25
+ start(): void;
26
+ dispose(): void;
27
+ runNow(): Promise<void>;
28
+ }
29
+ type MonitorDependencies = {
30
+ loadConfig: () => QuotaNotificationsConfig;
31
+ loadStorage: () => Promise<AccountStorageV3 | null>;
32
+ fetchSummary: (storage: AccountStorageV3, account: AccountMetadataV3 | undefined, onCredentialsPersisted: () => void) => Promise<AccountQuotaSummary | null>;
33
+ notify: DesktopNotifier;
34
+ notificationsSupported: () => boolean;
35
+ /**
36
+ * Called once per check when refreshing an account rotated its single-use
37
+ * refresh token to disk. The host wires this to its account-manager cache
38
+ * invalidation; see {@link fetchUsageForAccount} for why it is mandatory.
39
+ */
40
+ onCredentialsPersisted: () => void;
41
+ now: () => number;
42
+ initialDelayMs: number;
43
+ };
44
+ export declare function aggregateQuotaUsage(summaries: readonly AccountQuotaSummary[], now?: number): AggregatedQuotaUsage;
45
+ /**
46
+ * Decide whether this observation crosses a threshold downward.
47
+ *
48
+ * A threshold fires when the window is at or below it *and* the previous
49
+ * observation was strictly above it, which is the "rises above it after a
50
+ * reset" contract in `docs/configuration.md`. The most severe matching
51
+ * threshold wins, and the selection does not assume `thresholds` is sorted.
52
+ */
53
+ export declare function transitionQuotaWindow(previous: QuotaWindowNotificationState, current: AggregatedQuotaWindow, thresholds: readonly number[]): {
54
+ state: QuotaWindowNotificationState;
55
+ threshold?: number;
56
+ };
57
+ export declare function transitionQuotaState(previous: QuotaNotificationState | undefined, usage: AggregatedQuotaUsage, thresholds: readonly number[], now?: number): {
58
+ state: QuotaNotificationState;
59
+ crossings: QuotaThresholdCrossing[];
60
+ };
61
+ export declare function formatQuotaNotification(usage: AggregatedQuotaUsage): string;
62
+ export declare function createQuotaMonitor(overrides?: Partial<MonitorDependencies>): QuotaMonitor;
63
+ export {};
64
+ //# sourceMappingURL=quota-notifications.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"quota-notifications.d.ts","sourceRoot":"","sources":["../../lib/quota-notifications.ts"],"names":[],"mappings":"AAAA,OAAO,EAA2C,KAAK,wBAAwB,EAAE,MAAM,aAAa,CAAC;AACrG,OAAO,EASN,KAAK,iBAAiB,EACtB,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EAGN,KAAK,eAAe,EACpB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAGN,KAAK,sBAAsB,EAC3B,KAAK,4BAA4B,EACjC,MAAM,+BAA+B,CAAC;AAEvC,OAAO,EAAgB,KAAK,iBAAiB,EAAE,KAAK,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAM3F,MAAM,MAAM,eAAe,GAAG,UAAU,GAAG,QAAQ,CAAC;AAEpD,MAAM,WAAW,qBAAqB;IACrC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,oBAAoB;IACpC,QAAQ,EAAE,qBAAqB,CAAC;IAChC,MAAM,EAAE,qBAAqB,CAAC;CAC9B;AAED,MAAM,WAAW,sBAAsB;IACtC,MAAM,EAAE,eAAe,CAAC;IACxB,SAAS,EAAE,MAAM,CAAC;IAClB,gBAAgB,EAAE,MAAM,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,mBAAmB;IACnC,KAAK,EAAE,iBAAiB,CAAC;CACzB;AAED,MAAM,WAAW,YAAY;IAC5B,KAAK,IAAI,IAAI,CAAC;IACd,OAAO,IAAI,IAAI,CAAC;IAChB,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAED,KAAK,mBAAmB,GAAG;IAC1B,UAAU,EAAE,MAAM,wBAAwB,CAAC;IAC3C,WAAW,EAAE,MAAM,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAAC;IACpD,YAAY,EAAE,CACb,OAAO,EAAE,gBAAgB,EACzB,OAAO,EAAE,iBAAiB,GAAG,SAAS,EACtC,sBAAsB,EAAE,MAAM,IAAI,KAC9B,OAAO,CAAC,mBAAmB,GAAG,IAAI,CAAC,CAAC;IACzC,MAAM,EAAE,eAAe,CAAC;IACxB,sBAAsB,EAAE,MAAM,OAAO,CAAC;IACtC;;;;OAIG;IACH,sBAAsB,EAAE,MAAM,IAAI,CAAC;IACnC,GAAG,EAAE,MAAM,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;CACvB,CAAC;AAgDF,wBAAgB,mBAAmB,CAClC,SAAS,EAAE,SAAS,mBAAmB,EAAE,EACzC,GAAG,SAAa,GACd,oBAAoB,CAKtB;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CACpC,QAAQ,EAAE,4BAA4B,EACtC,OAAO,EAAE,qBAAqB,EAC9B,UAAU,EAAE,SAAS,MAAM,EAAE,GAC3B;IAAE,KAAK,EAAE,4BAA4B,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,CAa7D;AAED,wBAAgB,oBAAoB,CACnC,QAAQ,EAAE,sBAAsB,GAAG,SAAS,EAC5C,KAAK,EAAE,oBAAoB,EAC3B,UAAU,EAAE,SAAS,MAAM,EAAE,EAC7B,GAAG,SAAa,GACd;IAAE,KAAK,EAAE,sBAAsB,CAAC;IAAC,SAAS,EAAE,sBAAsB,EAAE,CAAA;CAAE,CA8BxE;AAED,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,oBAAoB,GAAG,MAAM,CAQ3E;AAUD,wBAAgB,kBAAkB,CAAC,SAAS,GAAE,OAAO,CAAC,mBAAmB,CAAM,GAAG,YAAY,CA0O7F"}
@@ -0,0 +1,371 @@
1
+ import { getQuotaNotifications, loadPluginConfig } from "./config.js";
2
+ import { deduplicateUsageAccountIndices, ensureCodexUsageAccessToken, fetchCodexUsage, formatUsageReset, getUsageLeftPercent, hasUsageWindow, parseCodexUsagePayload, resolveCodexUsageAccountId, } from "./codex-usage.js";
3
+ import { logDebug, logInfo, logWarn } from "./logger.js";
4
+ import { isDesktopNotificationSupported, sendDesktopNotification, } from "./desktop-notifications.js";
5
+ import { getQuotaNotificationStatePath, updateQuotaNotificationState, } from "./quota-notification-state.js";
6
+ import { registerCleanup, unregisterCleanup } from "./shutdown.js";
7
+ import { loadAccounts } from "./storage.js";
8
+ import { getStoragePath } from "./storage/state.js";
9
+ const INITIAL_CHECK_DELAY_MS = 10_000;
10
+ const MAX_CONCURRENCY = 2;
11
+ /**
12
+ * Reduce one quota window across accounts to the single account that has the
13
+ * most room left, reporting that account's own reset time.
14
+ *
15
+ * Two rules matter here:
16
+ *
17
+ * - A window the plan has switched off is skipped via {@link hasUsageWindow}.
18
+ * Such a window still reports `used_percent: 0`, so counting it would score
19
+ * a disabled window as 100% remaining and mask every other account.
20
+ * - The percentage and the reset time are taken from the *same* account. A
21
+ * max-percent/min-reset pair describes a quota no account actually has.
22
+ */
23
+ function aggregateWindow(summaries, select, now) {
24
+ let best;
25
+ for (const accountSummary of summaries) {
26
+ const window = select(accountSummary.usage);
27
+ if (!hasUsageWindow(window))
28
+ continue;
29
+ const remaining = getUsageLeftPercent(window.usedPercent);
30
+ if (remaining === undefined)
31
+ continue;
32
+ const resetAtMs = typeof window.resetAtMs === "number" &&
33
+ Number.isFinite(window.resetAtMs) &&
34
+ window.resetAtMs > now
35
+ ? window.resetAtMs
36
+ : undefined;
37
+ if (best === undefined || remaining > best.remainingPercent) {
38
+ best = { remainingPercent: remaining, resetAtMs };
39
+ continue;
40
+ }
41
+ // Tie on headroom: prefer the account that recovers first, and prefer a
42
+ // known reset over an unknown one.
43
+ if (remaining === best.remainingPercent &&
44
+ resetAtMs !== undefined &&
45
+ (best.resetAtMs === undefined || resetAtMs < best.resetAtMs)) {
46
+ best = { remainingPercent: remaining, resetAtMs };
47
+ }
48
+ }
49
+ return best ? { remainingPercent: best.remainingPercent, resetAtMs: best.resetAtMs } : {};
50
+ }
51
+ export function aggregateQuotaUsage(summaries, now = Date.now()) {
52
+ return {
53
+ fiveHour: aggregateWindow(summaries, (summary) => summary.primary, now),
54
+ weekly: aggregateWindow(summaries, (summary) => summary.secondary, now),
55
+ };
56
+ }
57
+ /**
58
+ * Decide whether this observation crosses a threshold downward.
59
+ *
60
+ * A threshold fires when the window is at or below it *and* the previous
61
+ * observation was strictly above it, which is the "rises above it after a
62
+ * reset" contract in `docs/configuration.md`. The most severe matching
63
+ * threshold wins, and the selection does not assume `thresholds` is sorted.
64
+ */
65
+ export function transitionQuotaWindow(previous, current, thresholds) {
66
+ const remaining = current.remainingPercent;
67
+ if (remaining === undefined)
68
+ return { state: previous };
69
+ const matching = thresholds.filter((threshold) => remaining <= threshold &&
70
+ (previous.lastPercent === undefined || previous.lastPercent > threshold));
71
+ return {
72
+ state: { lastPercent: remaining },
73
+ threshold: matching.length > 0 ? Math.min(...matching) : undefined,
74
+ };
75
+ }
76
+ export function transitionQuotaState(previous, usage, thresholds, now = Date.now()) {
77
+ const baseline = previous ?? { fiveHour: {}, weekly: {}, updatedAt: now };
78
+ const fiveHour = transitionQuotaWindow(baseline.fiveHour, usage.fiveHour, thresholds);
79
+ const weekly = transitionQuotaWindow(baseline.weekly, usage.weekly, thresholds);
80
+ const crossings = [];
81
+ if (fiveHour.threshold !== undefined && usage.fiveHour.remainingPercent !== undefined) {
82
+ crossings.push({
83
+ window: "fiveHour",
84
+ threshold: fiveHour.threshold,
85
+ remainingPercent: usage.fiveHour.remainingPercent,
86
+ resetAtMs: usage.fiveHour.resetAtMs,
87
+ });
88
+ }
89
+ if (weekly.threshold !== undefined && usage.weekly.remainingPercent !== undefined) {
90
+ crossings.push({
91
+ window: "weekly",
92
+ threshold: weekly.threshold,
93
+ remainingPercent: usage.weekly.remainingPercent,
94
+ resetAtMs: usage.weekly.resetAtMs,
95
+ });
96
+ }
97
+ return {
98
+ state: {
99
+ fiveHour: fiveHour.state,
100
+ weekly: weekly.state,
101
+ lastDeliveredAt: baseline.lastDeliveredAt,
102
+ updatedAt: now,
103
+ },
104
+ crossings,
105
+ };
106
+ }
107
+ export function formatQuotaNotification(usage) {
108
+ const fiveHourLine = usage.fiveHour.remainingPercent !== undefined
109
+ ? `5h: ${usage.fiveHour.remainingPercent}% | resets ${formatUsageReset(usage.fiveHour.resetAtMs) ?? "unavailable"}`
110
+ : "5h: unavailable";
111
+ const weeklyLine = usage.weekly.remainingPercent !== undefined
112
+ ? `Weekly: ${usage.weekly.remainingPercent}% | resets ${formatUsageReset(usage.weekly.resetAtMs) ?? "unavailable"}`
113
+ : "Weekly: unavailable";
114
+ return `${fiveHourLine}\n${weeklyLine}`;
115
+ }
116
+ export function createQuotaMonitor(overrides = {}) {
117
+ const dependencies = {
118
+ loadConfig: () => getQuotaNotifications(loadPluginConfig()),
119
+ loadStorage: loadAccounts,
120
+ fetchSummary: fetchUsageForAccount,
121
+ notify: sendDesktopNotification,
122
+ notificationsSupported: isDesktopNotificationSupported,
123
+ onCredentialsPersisted: () => undefined,
124
+ now: Date.now,
125
+ initialDelayMs: INITIAL_CHECK_DELAY_MS,
126
+ ...overrides,
127
+ };
128
+ let timer;
129
+ let started = false;
130
+ let disposed = false;
131
+ let running = false;
132
+ let generation = 0;
133
+ // Keyed by state path: the host flips `setStoragePath` at runtime, so one
134
+ // project's threshold state must never be carried into another's file.
135
+ const memoryStateByPath = new Map();
136
+ const schedule = (delayMs, expectedGeneration) => {
137
+ if (!started || disposed || expectedGeneration !== generation)
138
+ return;
139
+ if (timer)
140
+ clearTimeout(timer);
141
+ timer = setTimeout(() => {
142
+ timer = undefined;
143
+ void tick(expectedGeneration, true);
144
+ }, delayMs);
145
+ timer.unref();
146
+ };
147
+ const releaseUndeliveredClaim = async (statePath, claim, claimedAt) => {
148
+ const reverted = {
149
+ fiveHour: claim.state.fiveHour,
150
+ weekly: claim.state.weekly,
151
+ lastDeliveredAt: claim.previousLastDeliveredAt > 0 ? claim.previousLastDeliveredAt : undefined,
152
+ updatedAt: dependencies.now(),
153
+ };
154
+ for (const crossing of claim.crossings) {
155
+ reverted[crossing.window] = claim.baseline?.[crossing.window] ?? {};
156
+ }
157
+ try {
158
+ const applied = await updateQuotaNotificationState(statePath, (persisted) => {
159
+ if (persisted?.updatedAt !== claimedAt) {
160
+ // Someone else wrote after our claim; leave their state alone.
161
+ return { state: persisted ?? claim.state, result: false };
162
+ }
163
+ return { state: reverted, result: true };
164
+ });
165
+ if (applied)
166
+ memoryStateByPath.set(statePath, reverted);
167
+ }
168
+ catch (error) {
169
+ logWarn(`Failed to release an undelivered quota notification claim: ${error.message}`);
170
+ }
171
+ };
172
+ const check = async (config, expectedGeneration) => {
173
+ const storage = await dependencies.loadStorage();
174
+ if (!storage || storage.accounts.length === 0 || disposed || expectedGeneration !== generation)
175
+ return;
176
+ // Resolved next to the load that produced `storage`, not after the
177
+ // fetches: the host flips `setStoragePath` when the project changes, and
178
+ // this aggregate describes the accounts that were just read.
179
+ const statePath = getQuotaNotificationStatePath(getStoragePath());
180
+ const indices = deduplicateUsageAccountIndices(storage);
181
+ const summaries = [];
182
+ // Fired at the moment of rotation, not at the end of the check. The
183
+ // rotated refresh token is already durable on disk, and the host's
184
+ // cached AccountManager has a 500ms debounced save still holding the old
185
+ // one; a check over N accounts takes far longer than that, so deferring
186
+ // this leaves exactly the lost-update window open that it exists to
187
+ // close. Cheap to call more than once: it is a no-op with no cached
188
+ // manager.
189
+ const markCredentialsPersisted = () => {
190
+ try {
191
+ dependencies.onCredentialsPersisted();
192
+ }
193
+ catch (error) {
194
+ logWarn(`Failed to invalidate account cache after a quota refresh: ${error.message}`);
195
+ }
196
+ };
197
+ for (let offset = 0; offset < indices.length; offset += MAX_CONCURRENCY) {
198
+ if (disposed || expectedGeneration !== generation)
199
+ break;
200
+ const chunk = indices.slice(offset, offset + MAX_CONCURRENCY);
201
+ const results = await Promise.all(chunk.map((index) => dependencies.fetchSummary(storage, storage.accounts[index], markCredentialsPersisted)));
202
+ for (const summary of results) {
203
+ if (summary)
204
+ summaries.push(summary);
205
+ }
206
+ }
207
+ if (summaries.length === 0 || disposed || expectedGeneration !== generation)
208
+ return;
209
+ const now = dependencies.now();
210
+ const usage = aggregateQuotaUsage(summaries, now);
211
+ const memoryState = memoryStateByPath.get(statePath);
212
+ let claim;
213
+ try {
214
+ // Phase 1: claim the delivery slot under the cross-process lease. The
215
+ // notifier is NOT called here — its 10s timeout is an order of
216
+ // magnitude longer than the lease's ~660ms retry budget, so holding
217
+ // the lease across delivery makes concurrent processes drop whole
218
+ // checks with ELOCKED.
219
+ claim = await updateQuotaNotificationState(statePath, (persisted) => {
220
+ const baseline = memoryState && (!persisted || memoryState.updatedAt > persisted.updatedAt)
221
+ ? memoryState
222
+ : persisted;
223
+ const transition = transitionQuotaState(baseline, usage, config.thresholds, now);
224
+ const previousLastDeliveredAt = Math.max(persisted?.lastDeliveredAt ?? 0, memoryState?.lastDeliveredAt ?? 0);
225
+ if (previousLastDeliveredAt > 0) {
226
+ transition.state.lastDeliveredAt = previousLastDeliveredAt;
227
+ }
228
+ const everyCheckDue = config.notifyEveryCheck &&
229
+ (previousLastDeliveredAt === 0 || now - previousLastDeliveredAt >= config.intervalMs);
230
+ const delivering = transition.crossings.length > 0 || everyCheckDue;
231
+ if (delivering)
232
+ transition.state.lastDeliveredAt = now;
233
+ return {
234
+ state: transition.state,
235
+ result: {
236
+ delivering,
237
+ state: transition.state,
238
+ baseline,
239
+ crossings: transition.crossings,
240
+ previousLastDeliveredAt,
241
+ },
242
+ };
243
+ });
244
+ }
245
+ catch (error) {
246
+ logWarn(`Failed to update quota notification state: ${error.message}`);
247
+ return;
248
+ }
249
+ memoryStateByPath.set(statePath, claim.state);
250
+ if (!claim.delivering)
251
+ return;
252
+ // Phase 2: deliver outside the lease.
253
+ const delivered = disposed || expectedGeneration !== generation
254
+ ? false
255
+ : await dependencies
256
+ .notify("Codex quota status", formatQuotaNotification(usage))
257
+ .catch((error) => {
258
+ logDebug(`Failed to deliver quota notification: ${error.message}`);
259
+ return false;
260
+ });
261
+ if (delivered) {
262
+ logInfo("Quota notification delivered");
263
+ return;
264
+ }
265
+ // Phase 3: delivery failed, so release the claim and re-arm the crossed
266
+ // windows. Another process that claimed in the meantime keeps its claim.
267
+ await releaseUndeliveredClaim(statePath, claim, now);
268
+ };
269
+ const scheduleNext = (expectedGeneration) => {
270
+ let intervalMs;
271
+ try {
272
+ intervalMs = dependencies.loadConfig().intervalMs;
273
+ }
274
+ catch (error) {
275
+ // Reading the config is the only thing that can throw out here, and
276
+ // `tick`'s catch cannot cover its own `finally`.
277
+ logDebug(`Quota monitor could not schedule the next check: ${error.message}`);
278
+ return;
279
+ }
280
+ schedule(intervalMs, expectedGeneration);
281
+ };
282
+ const tick = async (expectedGeneration, reschedule) => {
283
+ if (disposed || expectedGeneration !== generation)
284
+ return;
285
+ if (running) {
286
+ if (reschedule)
287
+ scheduleNext(expectedGeneration);
288
+ return;
289
+ }
290
+ running = true;
291
+ // Stays true when the config read throws, so a transient failure retries
292
+ // on the next interval instead of killing the monitor for the session.
293
+ let keepPolling = true;
294
+ try {
295
+ const config = dependencies.loadConfig();
296
+ // `thresholds: []` with `notifyEveryCheck: false` can never deliver
297
+ // anything, so polling it would query the usage endpoint for every
298
+ // account, forever, with no possible output.
299
+ const canDeliver = config.notifyEveryCheck || config.thresholds.length > 0;
300
+ keepPolling = config.enabled && canDeliver && dependencies.notificationsSupported();
301
+ if (keepPolling)
302
+ await check(config, expectedGeneration);
303
+ }
304
+ catch (error) {
305
+ logDebug(`Quota monitor tick failed: ${error.message}`);
306
+ }
307
+ finally {
308
+ running = false;
309
+ // A feature that is switched off, or a platform without Notification
310
+ // Center, stops polling rather than waking every interval forever.
311
+ // Turning it on is a restart, which `docs/configuration.md` requires.
312
+ if (reschedule && keepPolling)
313
+ scheduleNext(expectedGeneration);
314
+ }
315
+ };
316
+ const disposeMonitor = () => {
317
+ if (disposed)
318
+ return;
319
+ disposed = true;
320
+ started = false;
321
+ generation += 1;
322
+ if (timer)
323
+ clearTimeout(timer);
324
+ timer = undefined;
325
+ unregisterCleanup(disposeMonitor);
326
+ };
327
+ return {
328
+ start() {
329
+ if (started && !disposed)
330
+ return;
331
+ started = true;
332
+ disposed = false;
333
+ generation += 1;
334
+ // `server.instance.disposed` is not emitted on SIGINT/SIGTERM, nor by
335
+ // every host, so the timer is also torn down by the shared drain.
336
+ registerCleanup(disposeMonitor);
337
+ schedule(dependencies.initialDelayMs, generation);
338
+ },
339
+ dispose: disposeMonitor,
340
+ async runNow() {
341
+ await tick(generation, false);
342
+ },
343
+ };
344
+ }
345
+ async function fetchUsageForAccount(storage, account, onCredentialsPersisted = () => undefined) {
346
+ if (!account)
347
+ return null;
348
+ try {
349
+ const credentials = await ensureCodexUsageAccessToken({ storage, account });
350
+ // Signal BEFORE the usage fetch: the rotation is already durable, so a
351
+ // later throw must not swallow the cache invalidation.
352
+ if (credentials.persisted)
353
+ onCredentialsPersisted();
354
+ const accountId = resolveCodexUsageAccountId({ account, accessToken: credentials.accessToken });
355
+ if (!accountId)
356
+ return null;
357
+ return {
358
+ usage: parseCodexUsagePayload(await fetchCodexUsage({
359
+ accountId,
360
+ accessToken: credentials.accessToken,
361
+ organizationId: account.organizationId,
362
+ normalizeAccountErrors: true,
363
+ })),
364
+ };
365
+ }
366
+ catch (error) {
367
+ logDebug(`Failed to fetch quota for one account: ${error.message}`);
368
+ return null;
369
+ }
370
+ }
371
+ //# sourceMappingURL=quota-notifications.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"quota-notifications.js","sourceRoot":"","sources":["../../lib/quota-notifications.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,gBAAgB,EAAiC,MAAM,aAAa,CAAC;AACrG,OAAO,EACN,8BAA8B,EAC9B,2BAA2B,EAC3B,eAAe,EACf,gBAAgB,EAChB,mBAAmB,EACnB,cAAc,EACd,sBAAsB,EACtB,0BAA0B,GAE1B,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AACzD,OAAO,EACN,8BAA8B,EAC9B,uBAAuB,GAEvB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EACN,6BAA6B,EAC7B,4BAA4B,GAG5B,MAAM,+BAA+B,CAAC;AACvC,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AACnE,OAAO,EAAE,YAAY,EAAiD,MAAM,cAAc,CAAC;AAC3F,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAEpD,MAAM,sBAAsB,GAAG,MAAM,CAAC;AACtC,MAAM,eAAe,GAAG,CAAC,CAAC;AAmD1B;;;;;;;;;;;GAWG;AACH,SAAS,eAAe,CACvB,SAAyC,EACzC,MAAoE,EACpE,GAAW;IAEX,IAAI,IAAkE,CAAC;IACvE,KAAK,MAAM,cAAc,IAAI,SAAS,EAAE,CAAC;QACxC,MAAM,MAAM,GAAG,MAAM,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC;QAC5C,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC;YAAE,SAAS;QACtC,MAAM,SAAS,GAAG,mBAAmB,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;QAC1D,IAAI,SAAS,KAAK,SAAS;YAAE,SAAS;QACtC,MAAM,SAAS,GACd,OAAO,MAAM,CAAC,SAAS,KAAK,QAAQ;YACpC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,SAAS,CAAC;YACjC,MAAM,CAAC,SAAS,GAAG,GAAG;YACrB,CAAC,CAAC,MAAM,CAAC,SAAS;YAClB,CAAC,CAAC,SAAS,CAAC;QACd,IAAI,IAAI,KAAK,SAAS,IAAI,SAAS,GAAG,IAAI,CAAC,gBAAgB,EAAE,CAAC;YAC7D,IAAI,GAAG,EAAE,gBAAgB,EAAE,SAAS,EAAE,SAAS,EAAE,CAAC;YAClD,SAAS;QACV,CAAC;QACD,wEAAwE;QACxE,mCAAmC;QACnC,IACC,SAAS,KAAK,IAAI,CAAC,gBAAgB;YACnC,SAAS,KAAK,SAAS;YACvB,CAAC,IAAI,CAAC,SAAS,KAAK,SAAS,IAAI,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC,EAC3D,CAAC;YACF,IAAI,GAAG,EAAE,gBAAgB,EAAE,SAAS,EAAE,SAAS,EAAE,CAAC;QACnD,CAAC;IACF,CAAC;IACD,OAAO,IAAI,CAAC,CAAC,CAAC,EAAE,gBAAgB,EAAE,IAAI,CAAC,gBAAgB,EAAE,SAAS,EAAE,IAAI,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;AAC3F,CAAC;AAED,MAAM,UAAU,mBAAmB,CAClC,SAAyC,EACzC,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE;IAEhB,OAAO;QACN,QAAQ,EAAE,eAAe,CAAC,SAAS,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC;QACvE,MAAM,EAAE,eAAe,CAAC,SAAS,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,SAAS,EAAE,GAAG,CAAC;KACvE,CAAC;AACH,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,qBAAqB,CACpC,QAAsC,EACtC,OAA8B,EAC9B,UAA6B;IAE7B,MAAM,SAAS,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAC3C,IAAI,SAAS,KAAK,SAAS;QAAE,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC;IAExD,MAAM,QAAQ,GAAG,UAAU,CAAC,MAAM,CACjC,CAAC,SAAS,EAAE,EAAE,CACb,SAAS,IAAI,SAAS;QACtB,CAAC,QAAQ,CAAC,WAAW,KAAK,SAAS,IAAI,QAAQ,CAAC,WAAW,GAAG,SAAS,CAAC,CACzE,CAAC;IACF,OAAO;QACN,KAAK,EAAE,EAAE,WAAW,EAAE,SAAS,EAAE;QACjC,SAAS,EAAE,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC,SAAS;KAClE,CAAC;AACH,CAAC;AAED,MAAM,UAAU,oBAAoB,CACnC,QAA4C,EAC5C,KAA2B,EAC3B,UAA6B,EAC7B,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE;IAEhB,MAAM,QAAQ,GAAG,QAAQ,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,SAAS,EAAE,GAAG,EAAE,CAAC;IAC1E,MAAM,QAAQ,GAAG,qBAAqB,CAAC,QAAQ,CAAC,QAAQ,EAAE,KAAK,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;IACtF,MAAM,MAAM,GAAG,qBAAqB,CAAC,QAAQ,CAAC,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IAChF,MAAM,SAAS,GAA6B,EAAE,CAAC;IAC/C,IAAI,QAAQ,CAAC,SAAS,KAAK,SAAS,IAAI,KAAK,CAAC,QAAQ,CAAC,gBAAgB,KAAK,SAAS,EAAE,CAAC;QACvF,SAAS,CAAC,IAAI,CAAC;YACd,MAAM,EAAE,UAAU;YAClB,SAAS,EAAE,QAAQ,CAAC,SAAS;YAC7B,gBAAgB,EAAE,KAAK,CAAC,QAAQ,CAAC,gBAAgB;YACjD,SAAS,EAAE,KAAK,CAAC,QAAQ,CAAC,SAAS;SACnC,CAAC,CAAC;IACJ,CAAC;IACD,IAAI,MAAM,CAAC,SAAS,KAAK,SAAS,IAAI,KAAK,CAAC,MAAM,CAAC,gBAAgB,KAAK,SAAS,EAAE,CAAC;QACnF,SAAS,CAAC,IAAI,CAAC;YACd,MAAM,EAAE,QAAQ;YAChB,SAAS,EAAE,MAAM,CAAC,SAAS;YAC3B,gBAAgB,EAAE,KAAK,CAAC,MAAM,CAAC,gBAAgB;YAC/C,SAAS,EAAE,KAAK,CAAC,MAAM,CAAC,SAAS;SACjC,CAAC,CAAC;IACJ,CAAC;IACD,OAAO;QACN,KAAK,EAAE;YACN,QAAQ,EAAE,QAAQ,CAAC,KAAK;YACxB,MAAM,EAAE,MAAM,CAAC,KAAK;YACpB,eAAe,EAAE,QAAQ,CAAC,eAAe;YACzC,SAAS,EAAE,GAAG;SACd;QACD,SAAS;KACT,CAAC;AACH,CAAC;AAED,MAAM,UAAU,uBAAuB,CAAC,KAA2B;IAClE,MAAM,YAAY,GAAG,KAAK,CAAC,QAAQ,CAAC,gBAAgB,KAAK,SAAS;QACjE,CAAC,CAAC,OAAO,KAAK,CAAC,QAAQ,CAAC,gBAAgB,cAAc,gBAAgB,CAAC,KAAK,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,aAAa,EAAE;QACnH,CAAC,CAAC,iBAAiB,CAAC;IACrB,MAAM,UAAU,GAAG,KAAK,CAAC,MAAM,CAAC,gBAAgB,KAAK,SAAS;QAC7D,CAAC,CAAC,WAAW,KAAK,CAAC,MAAM,CAAC,gBAAgB,cAAc,gBAAgB,CAAC,KAAK,CAAC,MAAM,CAAC,SAAS,CAAC,IAAI,aAAa,EAAE;QACnH,CAAC,CAAC,qBAAqB,CAAC;IACzB,OAAO,GAAG,YAAY,KAAK,UAAU,EAAE,CAAC;AACzC,CAAC;AAUD,MAAM,UAAU,kBAAkB,CAAC,YAA0C,EAAE;IAC9E,MAAM,YAAY,GAAwB;QACzC,UAAU,EAAE,GAAG,EAAE,CAAC,qBAAqB,CAAC,gBAAgB,EAAE,CAAC;QAC3D,WAAW,EAAE,YAAY;QACzB,YAAY,EAAE,oBAAoB;QAClC,MAAM,EAAE,uBAAuB;QAC/B,sBAAsB,EAAE,8BAA8B;QACtD,sBAAsB,EAAE,GAAG,EAAE,CAAC,SAAS;QACvC,GAAG,EAAE,IAAI,CAAC,GAAG;QACb,cAAc,EAAE,sBAAsB;QACtC,GAAG,SAAS;KACZ,CAAC;IACF,IAAI,KAAiC,CAAC;IACtC,IAAI,OAAO,GAAG,KAAK,CAAC;IACpB,IAAI,QAAQ,GAAG,KAAK,CAAC;IACrB,IAAI,OAAO,GAAG,KAAK,CAAC;IACpB,IAAI,UAAU,GAAG,CAAC,CAAC;IACnB,0EAA0E;IAC1E,uEAAuE;IACvE,MAAM,iBAAiB,GAAG,IAAI,GAAG,EAAkC,CAAC;IAEpE,MAAM,QAAQ,GAAG,CAAC,OAAe,EAAE,kBAA0B,EAAQ,EAAE;QACtE,IAAI,CAAC,OAAO,IAAI,QAAQ,IAAI,kBAAkB,KAAK,UAAU;YAAE,OAAO;QACtE,IAAI,KAAK;YAAE,YAAY,CAAC,KAAK,CAAC,CAAC;QAC/B,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE;YACvB,KAAK,GAAG,SAAS,CAAC;YAClB,KAAK,IAAI,CAAC,kBAAkB,EAAE,IAAI,CAAC,CAAC;QACrC,CAAC,EAAE,OAAO,CAAC,CAAC;QACZ,KAAK,CAAC,KAAK,EAAE,CAAC;IACf,CAAC,CAAC;IAEF,MAAM,uBAAuB,GAAG,KAAK,EACpC,SAAiB,EACjB,KAAoB,EACpB,SAAiB,EACD,EAAE;QAClB,MAAM,QAAQ,GAA2B;YACxC,QAAQ,EAAE,KAAK,CAAC,KAAK,CAAC,QAAQ;YAC9B,MAAM,EAAE,KAAK,CAAC,KAAK,CAAC,MAAM;YAC1B,eAAe,EAAE,KAAK,CAAC,uBAAuB,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,uBAAuB,CAAC,CAAC,CAAC,SAAS;YAC9F,SAAS,EAAE,YAAY,CAAC,GAAG,EAAE;SAC7B,CAAC;QACF,KAAK,MAAM,QAAQ,IAAI,KAAK,CAAC,SAAS,EAAE,CAAC;YACxC,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC,GAAG,KAAK,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;QACrE,CAAC;QACD,IAAI,CAAC;YACJ,MAAM,OAAO,GAAG,MAAM,4BAA4B,CAAU,SAAS,EAAE,CAAC,SAAS,EAAE,EAAE;gBACpF,IAAI,SAAS,EAAE,SAAS,KAAK,SAAS,EAAE,CAAC;oBACxC,+DAA+D;oBAC/D,OAAO,EAAE,KAAK,EAAE,SAAS,IAAI,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC;gBAC3D,CAAC;gBACD,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;YAC1C,CAAC,CAAC,CAAC;YACH,IAAI,OAAO;gBAAE,iBAAiB,CAAC,GAAG,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;QACzD,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,OAAO,CAAC,8DAA+D,KAAe,CAAC,OAAO,EAAE,CAAC,CAAC;QACnG,CAAC;IACF,CAAC,CAAC;IAEF,MAAM,KAAK,GAAG,KAAK,EAAE,MAAgC,EAAE,kBAA0B,EAAiB,EAAE;QACnG,MAAM,OAAO,GAAG,MAAM,YAAY,CAAC,WAAW,EAAE,CAAC;QACjD,IAAI,CAAC,OAAO,IAAI,OAAO,CAAC,QAAQ,CAAC,MAAM,KAAK,CAAC,IAAI,QAAQ,IAAI,kBAAkB,KAAK,UAAU;YAAE,OAAO;QACvG,mEAAmE;QACnE,yEAAyE;QACzE,6DAA6D;QAC7D,MAAM,SAAS,GAAG,6BAA6B,CAAC,cAAc,EAAE,CAAC,CAAC;QAElE,MAAM,OAAO,GAAG,8BAA8B,CAAC,OAAO,CAAC,CAAC;QACxD,MAAM,SAAS,GAA0B,EAAE,CAAC;QAC5C,oEAAoE;QACpE,mEAAmE;QACnE,yEAAyE;QACzE,wEAAwE;QACxE,oEAAoE;QACpE,oEAAoE;QACpE,WAAW;QACX,MAAM,wBAAwB,GAAG,GAAS,EAAE;YAC3C,IAAI,CAAC;gBACJ,YAAY,CAAC,sBAAsB,EAAE,CAAC;YACvC,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBAChB,OAAO,CAAC,6DAA8D,KAAe,CAAC,OAAO,EAAE,CAAC,CAAC;YAClG,CAAC;QACF,CAAC,CAAC;QACF,KAAK,IAAI,MAAM,GAAG,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,MAAM,IAAI,eAAe,EAAE,CAAC;YACzE,IAAI,QAAQ,IAAI,kBAAkB,KAAK,UAAU;gBAAE,MAAM;YACzD,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,MAAM,EAAE,MAAM,GAAG,eAAe,CAAC,CAAC;YAC9D,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,GAAG,CAChC,KAAK,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,YAAY,CAAC,YAAY,CAC7C,OAAO,EACP,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,EACvB,wBAAwB,CACxB,CAAC,CACF,CAAC;YACF,KAAK,MAAM,OAAO,IAAI,OAAO,EAAE,CAAC;gBAC/B,IAAI,OAAO;oBAAE,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YACtC,CAAC;QACF,CAAC;QACD,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,IAAI,QAAQ,IAAI,kBAAkB,KAAK,UAAU;YAAE,OAAO;QAEpF,MAAM,GAAG,GAAG,YAAY,CAAC,GAAG,EAAE,CAAC;QAC/B,MAAM,KAAK,GAAG,mBAAmB,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC;QAClD,MAAM,WAAW,GAAG,iBAAiB,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QAErD,IAAI,KAAoB,CAAC;QACzB,IAAI,CAAC;YACJ,sEAAsE;YACtE,+DAA+D;YAC/D,oEAAoE;YACpE,kEAAkE;YAClE,uBAAuB;YACvB,KAAK,GAAG,MAAM,4BAA4B,CAAgB,SAAS,EAAE,CAAC,SAAS,EAAE,EAAE;gBAClF,MAAM,QAAQ,GACb,WAAW,IAAI,CAAC,CAAC,SAAS,IAAI,WAAW,CAAC,SAAS,GAAG,SAAS,CAAC,SAAS,CAAC;oBACzE,CAAC,CAAC,WAAW;oBACb,CAAC,CAAC,SAAS,CAAC;gBACd,MAAM,UAAU,GAAG,oBAAoB,CAAC,QAAQ,EAAE,KAAK,EAAE,MAAM,CAAC,UAAU,EAAE,GAAG,CAAC,CAAC;gBACjF,MAAM,uBAAuB,GAAG,IAAI,CAAC,GAAG,CACvC,SAAS,EAAE,eAAe,IAAI,CAAC,EAC/B,WAAW,EAAE,eAAe,IAAI,CAAC,CACjC,CAAC;gBACF,IAAI,uBAAuB,GAAG,CAAC,EAAE,CAAC;oBACjC,UAAU,CAAC,KAAK,CAAC,eAAe,GAAG,uBAAuB,CAAC;gBAC5D,CAAC;gBACD,MAAM,aAAa,GAClB,MAAM,CAAC,gBAAgB;oBACvB,CAAC,uBAAuB,KAAK,CAAC,IAAI,GAAG,GAAG,uBAAuB,IAAI,MAAM,CAAC,UAAU,CAAC,CAAC;gBACvF,MAAM,UAAU,GAAG,UAAU,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,IAAI,aAAa,CAAC;gBACpE,IAAI,UAAU;oBAAE,UAAU,CAAC,KAAK,CAAC,eAAe,GAAG,GAAG,CAAC;gBACvD,OAAO;oBACN,KAAK,EAAE,UAAU,CAAC,KAAK;oBACvB,MAAM,EAAE;wBACP,UAAU;wBACV,KAAK,EAAE,UAAU,CAAC,KAAK;wBACvB,QAAQ;wBACR,SAAS,EAAE,UAAU,CAAC,SAAS;wBAC/B,uBAAuB;qBACvB;iBACD,CAAC;YACH,CAAC,CAAC,CAAC;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,OAAO,CAAC,8CAA+C,KAAe,CAAC,OAAO,EAAE,CAAC,CAAC;YAClF,OAAO;QACR,CAAC;QACD,iBAAiB,CAAC,GAAG,CAAC,SAAS,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC;QAC9C,IAAI,CAAC,KAAK,CAAC,UAAU;YAAE,OAAO;QAE9B,sCAAsC;QACtC,MAAM,SAAS,GACd,QAAQ,IAAI,kBAAkB,KAAK,UAAU;YAC5C,CAAC,CAAC,KAAK;YACP,CAAC,CAAC,MAAM,YAAY;iBAClB,MAAM,CAAC,oBAAoB,EAAE,uBAAuB,CAAC,KAAK,CAAC,CAAC;iBAC5D,KAAK,CAAC,CAAC,KAAc,EAAE,EAAE;gBACzB,QAAQ,CAAC,yCAA0C,KAAe,CAAC,OAAO,EAAE,CAAC,CAAC;gBAC9E,OAAO,KAAK,CAAC;YACd,CAAC,CAAC,CAAC;QACN,IAAI,SAAS,EAAE,CAAC;YACf,OAAO,CAAC,8BAA8B,CAAC,CAAC;YACxC,OAAO;QACR,CAAC;QAED,wEAAwE;QACxE,yEAAyE;QACzE,MAAM,uBAAuB,CAAC,SAAS,EAAE,KAAK,EAAE,GAAG,CAAC,CAAC;IACtD,CAAC,CAAC;IAEF,MAAM,YAAY,GAAG,CAAC,kBAA0B,EAAQ,EAAE;QACzD,IAAI,UAAkB,CAAC;QACvB,IAAI,CAAC;YACJ,UAAU,GAAG,YAAY,CAAC,UAAU,EAAE,CAAC,UAAU,CAAC;QACnD,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,oEAAoE;YACpE,iDAAiD;YACjD,QAAQ,CAAC,oDAAqD,KAAe,CAAC,OAAO,EAAE,CAAC,CAAC;YACzF,OAAO;QACR,CAAC;QACD,QAAQ,CAAC,UAAU,EAAE,kBAAkB,CAAC,CAAC;IAC1C,CAAC,CAAC;IAEF,MAAM,IAAI,GAAG,KAAK,EAAE,kBAA0B,EAAE,UAAmB,EAAiB,EAAE;QACrF,IAAI,QAAQ,IAAI,kBAAkB,KAAK,UAAU;YAAE,OAAO;QAC1D,IAAI,OAAO,EAAE,CAAC;YACb,IAAI,UAAU;gBAAE,YAAY,CAAC,kBAAkB,CAAC,CAAC;YACjD,OAAO;QACR,CAAC;QACD,OAAO,GAAG,IAAI,CAAC;QACf,yEAAyE;QACzE,uEAAuE;QACvE,IAAI,WAAW,GAAG,IAAI,CAAC;QACvB,IAAI,CAAC;YACJ,MAAM,MAAM,GAAG,YAAY,CAAC,UAAU,EAAE,CAAC;YACzC,oEAAoE;YACpE,mEAAmE;YACnE,6CAA6C;YAC7C,MAAM,UAAU,GAAG,MAAM,CAAC,gBAAgB,IAAI,MAAM,CAAC,UAAU,CAAC,MAAM,GAAG,CAAC,CAAC;YAC3E,WAAW,GAAG,MAAM,CAAC,OAAO,IAAI,UAAU,IAAI,YAAY,CAAC,sBAAsB,EAAE,CAAC;YACpF,IAAI,WAAW;gBAAE,MAAM,KAAK,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC;QAC1D,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,QAAQ,CAAC,8BAA+B,KAAe,CAAC,OAAO,EAAE,CAAC,CAAC;QACpE,CAAC;gBAAS,CAAC;YACV,OAAO,GAAG,KAAK,CAAC;YAChB,qEAAqE;YACrE,mEAAmE;YACnE,sEAAsE;YACtE,IAAI,UAAU,IAAI,WAAW;gBAAE,YAAY,CAAC,kBAAkB,CAAC,CAAC;QACjE,CAAC;IACF,CAAC,CAAC;IAEF,MAAM,cAAc,GAAG,GAAS,EAAE;QACjC,IAAI,QAAQ;YAAE,OAAO;QACrB,QAAQ,GAAG,IAAI,CAAC;QAChB,OAAO,GAAG,KAAK,CAAC;QAChB,UAAU,IAAI,CAAC,CAAC;QAChB,IAAI,KAAK;YAAE,YAAY,CAAC,KAAK,CAAC,CAAC;QAC/B,KAAK,GAAG,SAAS,CAAC;QAClB,iBAAiB,CAAC,cAAc,CAAC,CAAC;IACnC,CAAC,CAAC;IAEF,OAAO;QACN,KAAK;YACJ,IAAI,OAAO,IAAI,CAAC,QAAQ;gBAAE,OAAO;YACjC,OAAO,GAAG,IAAI,CAAC;YACf,QAAQ,GAAG,KAAK,CAAC;YACjB,UAAU,IAAI,CAAC,CAAC;YAChB,sEAAsE;YACtE,kEAAkE;YAClE,eAAe,CAAC,cAAc,CAAC,CAAC;YAChC,QAAQ,CAAC,YAAY,CAAC,cAAc,EAAE,UAAU,CAAC,CAAC;QACnD,CAAC;QACD,OAAO,EAAE,cAAc;QACvB,KAAK,CAAC,MAAM;YACX,MAAM,IAAI,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;QAC/B,CAAC;KACD,CAAC;AACH,CAAC;AAED,KAAK,UAAU,oBAAoB,CAClC,OAAyB,EACzB,OAAsC,EACtC,yBAAqC,GAAG,EAAE,CAAC,SAAS;IAEpD,IAAI,CAAC,OAAO;QAAE,OAAO,IAAI,CAAC;IAC1B,IAAI,CAAC;QACJ,MAAM,WAAW,GAAG,MAAM,2BAA2B,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC;QAC5E,uEAAuE;QACvE,uDAAuD;QACvD,IAAI,WAAW,CAAC,SAAS;YAAE,sBAAsB,EAAE,CAAC;QACpD,MAAM,SAAS,GAAG,0BAA0B,CAAC,EAAE,OAAO,EAAE,WAAW,EAAE,WAAW,CAAC,WAAW,EAAE,CAAC,CAAC;QAChG,IAAI,CAAC,SAAS;YAAE,OAAO,IAAI,CAAC;QAC5B,OAAO;YACN,KAAK,EAAE,sBAAsB,CAAC,MAAM,eAAe,CAAC;gBACnD,SAAS;gBACT,WAAW,EAAE,WAAW,CAAC,WAAW;gBACpC,cAAc,EAAE,OAAO,CAAC,cAAc;gBACtC,sBAAsB,EAAE,IAAI;aAC5B,CAAC,CAAC;SACH,CAAC;IACH,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QAChB,QAAQ,CAAC,0CAA2C,KAAe,CAAC,OAAO,EAAE,CAAC,CAAC;QAC/E,OAAO,IAAI,CAAC;IACb,CAAC;AACF,CAAC"}