codex-multi-auth 2.12.0 → 2.13.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.
- package/.codex-plugin/plugin.json +1 -1
- package/README.md +7 -1
- package/dist/lib/accounts.d.ts +75 -0
- package/dist/lib/accounts.d.ts.map +1 -1
- package/dist/lib/accounts.js +213 -42
- package/dist/lib/codex-manager/account-manager-commands.d.ts.map +1 -1
- package/dist/lib/codex-manager/account-manager-commands.js +1 -0
- package/dist/lib/codex-manager/commands/limits.d.ts +21 -0
- package/dist/lib/codex-manager/commands/limits.d.ts.map +1 -0
- package/dist/lib/codex-manager/commands/limits.js +158 -0
- package/dist/lib/codex-manager/help.d.ts.map +1 -1
- package/dist/lib/codex-manager/help.js +1 -0
- package/dist/lib/codex-manager/persist-selected-account.d.ts.map +1 -1
- package/dist/lib/codex-manager/persist-selected-account.js +14 -0
- package/dist/lib/codex-manager.d.ts.map +1 -1
- package/dist/lib/codex-manager.js +12 -0
- package/dist/lib/forecast.d.ts.map +1 -1
- package/dist/lib/forecast.js +1 -14
- package/dist/lib/preemptive-quota-scheduler.d.ts +12 -0
- package/dist/lib/preemptive-quota-scheduler.d.ts.map +1 -1
- package/dist/lib/preemptive-quota-scheduler.js +21 -0
- package/dist/lib/redaction.d.ts +17 -0
- package/dist/lib/redaction.d.ts.map +1 -0
- package/dist/lib/redaction.js +28 -0
- package/dist/lib/runtime/rotation-storage-meta.d.ts.map +1 -1
- package/dist/lib/runtime/rotation-storage-meta.js +4 -1
- package/dist/lib/runtime-rotation-proxy.d.ts +13 -0
- package/dist/lib/runtime-rotation-proxy.d.ts.map +1 -1
- package/dist/lib/runtime-rotation-proxy.js +47 -7
- package/dist/lib/storage.d.ts +20 -2
- package/dist/lib/storage.d.ts.map +1 -1
- package/dist/lib/storage.js +69 -25
- package/package.json +1 -1
- package/scripts/codex-routing.js +1 -0
- package/scripts/codex.js +157 -2
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import { formatAccountLabel } from "../../accounts.js";
|
|
2
|
+
import { MODEL_FAMILIES } from "../../prompts/codex.js";
|
|
3
|
+
import { findQuotaCacheEntryForAccount } from "../../quota-readiness.js";
|
|
4
|
+
import { redactEmails } from "../../redaction.js";
|
|
5
|
+
const LIMITS_SCHEMA_VERSION = 1;
|
|
6
|
+
const LIMITS_REFRESH_MAX_AGE_MS = 5 * 60_000;
|
|
7
|
+
const LIMITS_USAGE = "Usage: codex-multi-auth limits --json [--refresh]";
|
|
8
|
+
/** Parse the intentionally small, JSON-only limits command surface. */
|
|
9
|
+
function parseLimitsOptions(args) {
|
|
10
|
+
const options = { json: false, refresh: false, help: false };
|
|
11
|
+
for (const arg of args) {
|
|
12
|
+
if (arg === "--json" || arg === "-j") {
|
|
13
|
+
options.json = true;
|
|
14
|
+
continue;
|
|
15
|
+
}
|
|
16
|
+
if (arg === "--refresh") {
|
|
17
|
+
options.refresh = true;
|
|
18
|
+
continue;
|
|
19
|
+
}
|
|
20
|
+
if (arg === "--help" || arg === "-h") {
|
|
21
|
+
options.help = true;
|
|
22
|
+
continue;
|
|
23
|
+
}
|
|
24
|
+
return { ok: false, message: `Unknown limits option: ${arg}` };
|
|
25
|
+
}
|
|
26
|
+
if (!options.json && !options.help) {
|
|
27
|
+
return { ok: false, message: LIMITS_USAGE };
|
|
28
|
+
}
|
|
29
|
+
return { ok: true, options };
|
|
30
|
+
}
|
|
31
|
+
/** Convert a cached quota window to the explicit-null public JSON contract. */
|
|
32
|
+
function publicWindow(window) {
|
|
33
|
+
return {
|
|
34
|
+
usedPercent: window.usedPercent ?? null,
|
|
35
|
+
windowMinutes: window.windowMinutes ?? null,
|
|
36
|
+
resetAtMs: window.resetAtMs ?? null,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
/** Remove internal probe metadata and stabilize optional quota fields. */
|
|
40
|
+
function publicQuotaEntry(entry) {
|
|
41
|
+
return {
|
|
42
|
+
updatedAt: entry.updatedAt,
|
|
43
|
+
status: entry.status,
|
|
44
|
+
planType: entry.planType ?? null,
|
|
45
|
+
primary: publicWindow(entry.primary),
|
|
46
|
+
secondary: publicWindow(entry.secondary),
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Emit configured accounts joined to safe cached quota records.
|
|
51
|
+
*
|
|
52
|
+
* Cached mode performs no provider requests. Refresh mode delegates to the
|
|
53
|
+
* existing sequential, age-gated refresh path before serializing the snapshot.
|
|
54
|
+
*/
|
|
55
|
+
export async function runLimitsCommand(args, deps) {
|
|
56
|
+
const parsed = parseLimitsOptions(args);
|
|
57
|
+
const logInfo = deps.logInfo ?? console.log;
|
|
58
|
+
const logError = deps.logError ?? console.error;
|
|
59
|
+
if (!parsed.ok) {
|
|
60
|
+
logError(parsed.message);
|
|
61
|
+
return 1;
|
|
62
|
+
}
|
|
63
|
+
if (parsed.options.help) {
|
|
64
|
+
logInfo(LIMITS_USAGE);
|
|
65
|
+
return 0;
|
|
66
|
+
}
|
|
67
|
+
deps.setStoragePath(null);
|
|
68
|
+
const storage = await deps.loadAccounts();
|
|
69
|
+
if (!storage || storage.accounts.length === 0) {
|
|
70
|
+
const generatedAt = deps.getNow?.() ?? Date.now();
|
|
71
|
+
logInfo(JSON.stringify({
|
|
72
|
+
schemaVersion: LIMITS_SCHEMA_VERSION,
|
|
73
|
+
generatedAt,
|
|
74
|
+
mode: parsed.options.refresh ? "refresh" : "cached",
|
|
75
|
+
// Keep the key set identical for an empty pool so a consumer
|
|
76
|
+
// never has to branch on its presence. There is no account to
|
|
77
|
+
// route to, so `routedIndex` is null rather than a positional 0
|
|
78
|
+
// that addresses nothing.
|
|
79
|
+
selection: {
|
|
80
|
+
pinnedIndex: null,
|
|
81
|
+
routedIndex: null,
|
|
82
|
+
activeIndexByFamily: {},
|
|
83
|
+
},
|
|
84
|
+
accounts: [],
|
|
85
|
+
}, null, 2));
|
|
86
|
+
return 0;
|
|
87
|
+
}
|
|
88
|
+
let cache = await deps.loadQuotaCache();
|
|
89
|
+
if (parsed.options.refresh) {
|
|
90
|
+
cache = await deps.refreshQuotaCache(storage, cache, LIMITS_REFRESH_MAX_AGE_MS);
|
|
91
|
+
}
|
|
92
|
+
const generatedAt = deps.getNow?.() ?? Date.now();
|
|
93
|
+
const selection = resolveSelection(storage, deps.resolveActiveIndex);
|
|
94
|
+
const accounts = storage.accounts.map((account, index) => {
|
|
95
|
+
const quota = findQuotaCacheEntryForAccount(cache, account, storage.accounts);
|
|
96
|
+
return {
|
|
97
|
+
index,
|
|
98
|
+
// Labels are built from the account email. Mask it, exactly as
|
|
99
|
+
// `forecast --json` already does, so a snapshot written to a log
|
|
100
|
+
// shipper or a ticket does not carry the address.
|
|
101
|
+
label: redactEmails(formatAccountLabel(account, index)),
|
|
102
|
+
enabled: account.enabled !== false,
|
|
103
|
+
current: index === selection.routedIndex,
|
|
104
|
+
quota: quota ? publicQuotaEntry(quota) : null,
|
|
105
|
+
};
|
|
106
|
+
});
|
|
107
|
+
logInfo(JSON.stringify({
|
|
108
|
+
schemaVersion: LIMITS_SCHEMA_VERSION,
|
|
109
|
+
generatedAt,
|
|
110
|
+
mode: parsed.options.refresh ? "refresh" : "cached",
|
|
111
|
+
selection,
|
|
112
|
+
accounts,
|
|
113
|
+
}, null, 2));
|
|
114
|
+
return 0;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* The CONFIGURED routing target, and the state that decides it.
|
|
118
|
+
*
|
|
119
|
+
* `current` cannot be `activeIndexByFamily.codex` alone. The runtime proxy
|
|
120
|
+
* routes on `pinnedAccountIndex` whenever a `switch` pin is set, and flows that
|
|
121
|
+
* move the active index without touching the pin (rotation saves, `unpin`, an
|
|
122
|
+
* ephemeral `--account`) would otherwise leave `current: true` on a row that is
|
|
123
|
+
* not the configured target. Both inputs are emitted so a consumer can tell
|
|
124
|
+
* which one applied, and the per-family map is emitted because a pool can hold
|
|
125
|
+
* a different active index per family.
|
|
126
|
+
*
|
|
127
|
+
* This is deliberately NOT a prediction of which account the next request
|
|
128
|
+
* lands on. The proxy skips an account that is disabled, inside a rate-limit
|
|
129
|
+
* window, cooling down, or behind an open circuit breaker, and it applies
|
|
130
|
+
* session affinity and an ephemeral `--account` override that never touch
|
|
131
|
+
* storage. Reproducing that here would mean a third copy of the selector
|
|
132
|
+
* (`why-selected` and `forecast` already own live selection), and a snapshot
|
|
133
|
+
* read from a cache cannot be authoritative about it in any case. Consumers
|
|
134
|
+
* that need liveness have `enabled` on every row and `why-selected --json`.
|
|
135
|
+
*
|
|
136
|
+
* `routedIndex` is null only for an empty pool, where no row is `current`.
|
|
137
|
+
*/
|
|
138
|
+
function resolveSelection(storage, resolveActiveIndex) {
|
|
139
|
+
if (storage.accounts.length === 0) {
|
|
140
|
+
return { pinnedIndex: null, routedIndex: null, activeIndexByFamily: {} };
|
|
141
|
+
}
|
|
142
|
+
const activeIndexByFamily = {};
|
|
143
|
+
for (const family of MODEL_FAMILIES) {
|
|
144
|
+
activeIndexByFamily[family] = resolveActiveIndex(storage, family);
|
|
145
|
+
}
|
|
146
|
+
const rawPin = storage.pinnedAccountIndex;
|
|
147
|
+
const pinnedIndex = typeof rawPin === "number" &&
|
|
148
|
+
Number.isInteger(rawPin) &&
|
|
149
|
+
rawPin >= 0 &&
|
|
150
|
+
rawPin < storage.accounts.length
|
|
151
|
+
? rawPin
|
|
152
|
+
: null;
|
|
153
|
+
return {
|
|
154
|
+
pinnedIndex,
|
|
155
|
+
routedIndex: pinnedIndex ?? activeIndexByFamily.codex ?? 0,
|
|
156
|
+
activeIndexByFamily,
|
|
157
|
+
};
|
|
158
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"help.d.ts","sourceRoot":"","sources":["../../../lib/codex-manager/help.ts"],"names":[],"mappings":"AAIA,wBAAgB,UAAU,IAAI,IAAI,
|
|
1
|
+
{"version":3,"file":"help.d.ts","sourceRoot":"","sources":["../../../lib/codex-manager/help.ts"],"names":[],"mappings":"AAIA,wBAAgB,UAAU,IAAI,IAAI,CAuDjC;AAED,MAAM,MAAM,gBAAgB,GAAG;IAC9B,MAAM,EAAE,OAAO,CAAC;IAChB,UAAU,EAAE,OAAO,CAAC;IACpB,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAC5B;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,OAAO,EAAE,gBAAgB,CAAA;CAAE,GACvC;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAC7B;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAEnD,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,mBAAmB,CAuGtE;AAED,UAAU,cAAc;IACvB,IAAI,EAAE,OAAO,CAAC;IACd,IAAI,EAAE,OAAO,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,aAAa,EAAE,OAAO,CAAC;CACvB;AAED,MAAM,MAAM,cAAc,GACvB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,OAAO,EAAE,cAAc,CAAA;CAAE,GACrC;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAC7B;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAEnD,wBAAgB,cAAc,IAAI,IAAI,CAgBrC;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,cAAc,CAyD5D"}
|
|
@@ -8,6 +8,7 @@ export function printUsage() {
|
|
|
8
8
|
" codex-multi-auth login [--device-auth|--manual|--no-browser] [--org <org_id>] [--preserve-selection] [--account <index|email|account_id>]",
|
|
9
9
|
" codex-multi-auth status [--json] (list is the same command)",
|
|
10
10
|
" codex-multi-auth check (always live-probes)",
|
|
11
|
+
" codex-multi-auth limits --json [--refresh] (structured quota windows; refresh is age-gated)",
|
|
11
12
|
"",
|
|
12
13
|
"Daily use:",
|
|
13
14
|
" codex-multi-auth list [--json]",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"persist-selected-account.d.ts","sourceRoot":"","sources":["../../../lib/codex-manager/persist-selected-account.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAC3D,OAAO,EACN,KAAK,gBAAgB,EAIrB,MAAM,eAAe,CAAC;AAQvB;;;;;;GAMG;AACH,wBAAsB,6BAA6B,CAAC,EACnD,OAAO,EACP,WAAW,EACX,MAAM,EACN,YAAY,EACZ,kBAAkB,EAClB,2BAAmC,EACnC,MAAc,EACd,QAAgB,EAChB,sBAA8B,GAC9B,EAAE;IACF,OAAO,EAAE,gBAAgB,CAAC;IAC1B,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,qBAAqB,CAAC;IACpC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,2BAA2B,CAAC,EAAE,OAAO,CAAC;IACtC,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,sBAAsB,CAAC,EAAE,OAAO,CAAC;CACjC,GAAG,OAAO,CAAC;IAAE,MAAM,EAAE,OAAO,CAAC;IAAC,WAAW,EAAE,OAAO,CAAA;CAAE,CAAC,
|
|
1
|
+
{"version":3,"file":"persist-selected-account.d.ts","sourceRoot":"","sources":["../../../lib/codex-manager/persist-selected-account.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAC3D,OAAO,EACN,KAAK,gBAAgB,EAIrB,MAAM,eAAe,CAAC;AAQvB;;;;;;GAMG;AACH,wBAAsB,6BAA6B,CAAC,EACnD,OAAO,EACP,WAAW,EACX,MAAM,EACN,YAAY,EACZ,kBAAkB,EAClB,2BAAmC,EACnC,MAAc,EACd,QAAgB,EAChB,sBAA8B,GAC9B,EAAE;IACF,OAAO,EAAE,gBAAgB,CAAC;IAC1B,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,qBAAqB,CAAC;IACpC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,2BAA2B,CAAC,EAAE,OAAO,CAAC;IACtC,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,sBAAsB,CAAC,EAAE,OAAO,CAAC;CACjC,GAAG,OAAO,CAAC;IAAE,MAAM,EAAE,OAAO,CAAC;IAAC,WAAW,EAAE,OAAO,CAAA;CAAE,CAAC,CAmHrD"}
|
|
@@ -77,6 +77,20 @@ export async function persistAndSyncSelectedAccount({ storage, targetIndex, pars
|
|
|
77
77
|
account.lastSwitchReason = switchReason;
|
|
78
78
|
if (setPin) {
|
|
79
79
|
storage.pinnedAccountIndex = targetIndex;
|
|
80
|
+
// A manual switch requests a fresh upstream attempt, including when
|
|
81
|
+
// reselecting the same account after an out-of-band quota reset. Real 429
|
|
82
|
+
// responses will populate these markers again.
|
|
83
|
+
//
|
|
84
|
+
// `setPin` is the right trigger because it is set only by `switch` (which
|
|
85
|
+
// the login dashboard's account picker also routes through), i.e. exactly
|
|
86
|
+
// the paths where a person picked this account. `best` passes `clearPin`
|
|
87
|
+
// and a backup restore passes neither, and neither of those is a request
|
|
88
|
+
// to revalidate a rate-limited account.
|
|
89
|
+
//
|
|
90
|
+
// Delete rather than assign `{}`: `AccountManager.buildStorageSnapshot`
|
|
91
|
+
// omits the field when the map is empty, so assigning would persist a
|
|
92
|
+
// second on-disk shape for the same state.
|
|
93
|
+
delete account.rateLimitResetTimes;
|
|
80
94
|
}
|
|
81
95
|
else if (clearPin) {
|
|
82
96
|
delete storage.pinnedAccountIndex;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"codex-manager.d.ts","sourceRoot":"","sources":["../../lib/codex-manager.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"codex-manager.d.ts","sourceRoot":"","sources":["../../lib/codex-manager.ts"],"names":[],"mappings":"AA4DA,OAAO,EAIN,yBAAyB,EACzB,MAAM,eAAe,CAAC;AA6BvB,OAAO,EAEN,mBAAmB,EAMnB,sBAAsB,EAGtB,MAAM,qCAAqC,CAAC;AA+B7C,OAAO,EAEN,KAAK,gBAAgB,EAWrB,MAAM,cAAc,CAAC;AAKtB,OAAO,EAAE,mBAAmB,EAAE,sBAAsB,EAAE,CAAC;AA4IvD,wBAAsB,4BAA4B,IAAI,OAAO,CAAC,OAAO,CAAC,CAuGrE;AACD,mFAAmF;AACnF,wBAAgB,wBAAwB,IAAI,CAC3C,OAAO,EAAE,gBAAgB,KACrB,OAAO,CAAC,UAAU,CAAC,OAAO,yBAAyB,CAAC,CAAC,CAwEzD;AA8PD,wBAAsB,oBAAoB,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CA4C7E"}
|
|
@@ -14,6 +14,7 @@ import { runBudgetCommand } from "./codex-manager/commands/budget.js";
|
|
|
14
14
|
import { runBridgeCommand } from "./codex-manager/commands/bridge.js";
|
|
15
15
|
import { runCheckCommand } from "./codex-manager/commands/check.js";
|
|
16
16
|
import { runIntegrationsCommand } from "./codex-manager/commands/integrations.js";
|
|
17
|
+
import { runLimitsCommand } from "./codex-manager/commands/limits.js";
|
|
17
18
|
import { runModelsCommand } from "./codex-manager/commands/models.js";
|
|
18
19
|
import { runMonitorCommand } from "./codex-manager/commands/monitor.js";
|
|
19
20
|
import { runConfigExplainCommand } from "./codex-manager/commands/config-explain.js";
|
|
@@ -37,6 +38,7 @@ import { runHistoryCommand } from "./codex-manager/commands/history.js";
|
|
|
37
38
|
import { runUnpinCommand } from "./codex-manager/commands/unpin.js";
|
|
38
39
|
import { runWorkspaceCommand } from "./codex-manager/commands/workspace.js";
|
|
39
40
|
import { runUsageCommand } from "./codex-manager/commands/usage.js";
|
|
41
|
+
import { refreshQuotaCacheForMenu } from "./codex-manager/login-menu-data.js";
|
|
40
42
|
import { printUsage } from "./codex-manager/help.js";
|
|
41
43
|
import { availabilityTone, formatBackupSavedAt, formatCompactQuotaSnapshot, formatRateLimitEntry, formatResultSummary, normalizeFailureDetail, riskTone, styleAccountDetailText, stylePromptText, styleQuotaSummary, } from "./codex-manager/formatters/index.js";
|
|
42
44
|
import { applyUiThemeFromDashboardSettings } from "./codex-manager/settings-hub.js";
|
|
@@ -376,6 +378,16 @@ const CLI_COMMAND_HANDLERS = new Map([
|
|
|
376
378
|
["login", (rest) => runAuthLogin(rest, { runForecast, createRepairCommandDeps })],
|
|
377
379
|
["list", runListOrStatusCommand],
|
|
378
380
|
["status", runListOrStatusCommand],
|
|
381
|
+
[
|
|
382
|
+
"limits",
|
|
383
|
+
(rest) => runLimitsCommand(rest, {
|
|
384
|
+
setStoragePath,
|
|
385
|
+
loadAccounts,
|
|
386
|
+
loadQuotaCache,
|
|
387
|
+
refreshQuotaCache: refreshQuotaCacheForMenu,
|
|
388
|
+
resolveActiveIndex,
|
|
389
|
+
}),
|
|
390
|
+
],
|
|
379
391
|
[
|
|
380
392
|
"switch",
|
|
381
393
|
(rest) => runSwitchCommand(rest, {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"forecast.d.ts","sourceRoot":"","sources":["../../lib/forecast.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAC3D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"forecast.d.ts","sourceRoot":"","sources":["../../lib/forecast.ts"],"names":[],"mappings":"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"}
|
package/dist/lib/forecast.js
CHANGED
|
@@ -1,25 +1,12 @@
|
|
|
1
1
|
import { AUTH_INVALIDATION_MARKER, formatAccountLabel, formatWaitTime, } from "./accounts.js";
|
|
2
2
|
import { findQuotaCacheEntryForAccount, isQuotaCacheEntryExhausted, quotaUsedPercentIsExhausted, } from "./quota-readiness.js";
|
|
3
|
+
import { redactEmails } from "./redaction.js";
|
|
3
4
|
import { getRateLimitResetTimeForFamily, getRateLimitResetTimeForModel, } from "./runtime/account-status.js";
|
|
4
5
|
function clampRisk(score) {
|
|
5
6
|
if (!Number.isFinite(score))
|
|
6
7
|
return 100;
|
|
7
8
|
return Math.max(0, Math.min(100, Math.round(score)));
|
|
8
9
|
}
|
|
9
|
-
function maskEmail(value) {
|
|
10
|
-
const atIndex = value.indexOf("@");
|
|
11
|
-
if (atIndex <= 0)
|
|
12
|
-
return "***@***";
|
|
13
|
-
const local = value.slice(0, atIndex);
|
|
14
|
-
const domain = value.slice(atIndex + 1);
|
|
15
|
-
const domainParts = domain.split(".");
|
|
16
|
-
const tld = domainParts.pop() ?? "";
|
|
17
|
-
const prefix = local.slice(0, Math.min(2, local.length));
|
|
18
|
-
return `${prefix}***@***.${tld || "***"}`;
|
|
19
|
-
}
|
|
20
|
-
function redactEmails(value) {
|
|
21
|
-
return value.replace(/[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/g, (match) => maskEmail(match));
|
|
22
|
-
}
|
|
23
10
|
function redactSensitiveReason(value) {
|
|
24
11
|
return redactEmails(value
|
|
25
12
|
.replace(/Bearer\s+\S+/gi, "Bearer ***")
|
|
@@ -48,6 +48,18 @@ export declare class PreemptiveQuotaScheduler {
|
|
|
48
48
|
update(key: string, snapshot: QuotaSchedulerSnapshot): void;
|
|
49
49
|
markRateLimited(key: string, retryAfterMs: number, now?: number): void;
|
|
50
50
|
getDeferral(key: string, now?: number): QuotaDeferralDecision;
|
|
51
|
+
/**
|
|
52
|
+
* Forget cached quota observations for one account across its model keys.
|
|
53
|
+
*
|
|
54
|
+
* @param prefix Account key INCLUDING its trailing separator, as produced by
|
|
55
|
+
* `buildQuotaScheduleAccountPrefix`. Without that separator the prefix also
|
|
56
|
+
* matches neighbouring identities (`account:email:foo` matches
|
|
57
|
+
* `account:email:foobar:codex`). An empty prefix is a no-op rather than a
|
|
58
|
+
* silent clear-all; use `clearAll()` when that is the intent.
|
|
59
|
+
*/
|
|
60
|
+
clearByPrefix(prefix: string): void;
|
|
61
|
+
/** Forget every cached quota observation. */
|
|
62
|
+
clearAll(): void;
|
|
51
63
|
prune(now?: number): number;
|
|
52
64
|
}
|
|
53
65
|
//# sourceMappingURL=preemptive-quota-scheduler.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"preemptive-quota-scheduler.d.ts","sourceRoot":"","sources":["../../lib/preemptive-quota-scheduler.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,oBAAoB;IACpC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,sBAAsB;IACtC,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,oBAAoB,CAAC;IAC9B,SAAS,EAAE,oBAAoB,CAAC;IAChC,SAAS,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,qBAAqB;IACrC,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,YAAY,GAAG,uBAAuB,CAAC;CAChD;AAED,MAAM,WAAW,qBAAqB;IACrC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,gCAAgC,CAAC,EAAE,MAAM,CAAC;IAC1C,kCAAkC,CAAC,EAAE,MAAM,CAAC;IAC5C,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,aAAa,CAAC,EAAE,MAAM,CAAC;CACvB;AA6GD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,SAAa,GAAG,sBAAsB,GAAG,IAAI,CAqB5H;AAED,qBAAa,wBAAwB;IACpC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA6C;IACvE,OAAO,CAAC,OAAO,CAAU;IACzB,OAAO,CAAC,gCAAgC,CAAS;IACjD,OAAO,CAAC,kCAAkC,CAAS;IACnD,OAAO,CAAC,aAAa,CAAS;IAC9B,OAAO,CAAC,WAAW,CAAK;gBAEZ,OAAO,GAAE,qBAA0B;IAQ/C,SAAS,CAAC,OAAO,GAAE,qBAA0B,GAAG,IAAI;IA2CpD,OAAO,CAAC,UAAU;IAMlB,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,sBAAsB,GAAG,IAAI;IAM3D,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,GAAG,SAAa,GAAG,IAAI;IA8B1E,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,SAAa,GAAG,qBAAqB;IAkEjE,KAAK,CAAC,GAAG,SAAa,GAAG,MAAM;CAW/B"}
|
|
1
|
+
{"version":3,"file":"preemptive-quota-scheduler.d.ts","sourceRoot":"","sources":["../../lib/preemptive-quota-scheduler.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,oBAAoB;IACpC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,sBAAsB;IACtC,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,oBAAoB,CAAC;IAC9B,SAAS,EAAE,oBAAoB,CAAC;IAChC,SAAS,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,qBAAqB;IACrC,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,YAAY,GAAG,uBAAuB,CAAC;CAChD;AAED,MAAM,WAAW,qBAAqB;IACrC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,gCAAgC,CAAC,EAAE,MAAM,CAAC;IAC1C,kCAAkC,CAAC,EAAE,MAAM,CAAC;IAC5C,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,aAAa,CAAC,EAAE,MAAM,CAAC;CACvB;AA6GD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,SAAa,GAAG,sBAAsB,GAAG,IAAI,CAqB5H;AAED,qBAAa,wBAAwB;IACpC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA6C;IACvE,OAAO,CAAC,OAAO,CAAU;IACzB,OAAO,CAAC,gCAAgC,CAAS;IACjD,OAAO,CAAC,kCAAkC,CAAS;IACnD,OAAO,CAAC,aAAa,CAAS;IAC9B,OAAO,CAAC,WAAW,CAAK;gBAEZ,OAAO,GAAE,qBAA0B;IAQ/C,SAAS,CAAC,OAAO,GAAE,qBAA0B,GAAG,IAAI;IA2CpD,OAAO,CAAC,UAAU;IAMlB,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,sBAAsB,GAAG,IAAI;IAM3D,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,GAAG,SAAa,GAAG,IAAI;IA8B1E,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,SAAa,GAAG,qBAAqB;IAkEjE;;;;;;;;OAQG;IACH,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAOnC,6CAA6C;IAC7C,QAAQ,IAAI,IAAI;IAIhB,KAAK,CAAC,GAAG,SAAa,GAAG,MAAM;CAW/B"}
|
|
@@ -264,6 +264,27 @@ export class PreemptiveQuotaScheduler {
|
|
|
264
264
|
}
|
|
265
265
|
return { defer: false, waitMs: 0 };
|
|
266
266
|
}
|
|
267
|
+
/**
|
|
268
|
+
* Forget cached quota observations for one account across its model keys.
|
|
269
|
+
*
|
|
270
|
+
* @param prefix Account key INCLUDING its trailing separator, as produced by
|
|
271
|
+
* `buildQuotaScheduleAccountPrefix`. Without that separator the prefix also
|
|
272
|
+
* matches neighbouring identities (`account:email:foo` matches
|
|
273
|
+
* `account:email:foobar:codex`). An empty prefix is a no-op rather than a
|
|
274
|
+
* silent clear-all; use `clearAll()` when that is the intent.
|
|
275
|
+
*/
|
|
276
|
+
clearByPrefix(prefix) {
|
|
277
|
+
if (!prefix)
|
|
278
|
+
return;
|
|
279
|
+
for (const key of this.snapshots.keys()) {
|
|
280
|
+
if (key.startsWith(prefix))
|
|
281
|
+
this.snapshots.delete(key);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
/** Forget every cached quota observation. */
|
|
285
|
+
clearAll() {
|
|
286
|
+
this.snapshots.clear();
|
|
287
|
+
}
|
|
267
288
|
prune(now = Date.now()) {
|
|
268
289
|
let removed = 0;
|
|
269
290
|
for (const [key, snapshot] of this.snapshots.entries()) {
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Email redaction for machine-readable command output.
|
|
3
|
+
*
|
|
4
|
+
* Account labels are built from the operator-visible email, so any command that
|
|
5
|
+
* emits them as JSON hands that address to whatever consumes the output: log
|
|
6
|
+
* shippers, dashboards, ticket attachments. `forecast --json` has masked them
|
|
7
|
+
* since it was written; this module is that same masking, shared so other JSON
|
|
8
|
+
* surfaces do not each re-derive it.
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Mask one address, keeping just enough to tell two accounts apart: the first
|
|
12
|
+
* two characters of the local part and the TLD.
|
|
13
|
+
*/
|
|
14
|
+
export declare function maskEmail(value: string): string;
|
|
15
|
+
/** Mask every address embedded in a free-text string. */
|
|
16
|
+
export declare function redactEmails(value: string): string;
|
|
17
|
+
//# sourceMappingURL=redaction.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redaction.d.ts","sourceRoot":"","sources":["../../lib/redaction.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH;;;GAGG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAS/C;AAED,yDAAyD;AACzD,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAKlD"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Email redaction for machine-readable command output.
|
|
3
|
+
*
|
|
4
|
+
* Account labels are built from the operator-visible email, so any command that
|
|
5
|
+
* emits them as JSON hands that address to whatever consumes the output: log
|
|
6
|
+
* shippers, dashboards, ticket attachments. `forecast --json` has masked them
|
|
7
|
+
* since it was written; this module is that same masking, shared so other JSON
|
|
8
|
+
* surfaces do not each re-derive it.
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Mask one address, keeping just enough to tell two accounts apart: the first
|
|
12
|
+
* two characters of the local part and the TLD.
|
|
13
|
+
*/
|
|
14
|
+
export function maskEmail(value) {
|
|
15
|
+
const atIndex = value.indexOf("@");
|
|
16
|
+
if (atIndex <= 0)
|
|
17
|
+
return "***@***";
|
|
18
|
+
const local = value.slice(0, atIndex);
|
|
19
|
+
const domain = value.slice(atIndex + 1);
|
|
20
|
+
const domainParts = domain.split(".");
|
|
21
|
+
const tld = domainParts.pop() ?? "";
|
|
22
|
+
const prefix = local.slice(0, Math.min(2, local.length));
|
|
23
|
+
return `${prefix}***@***.${tld || "***"}`;
|
|
24
|
+
}
|
|
25
|
+
/** Mask every address embedded in a free-text string. */
|
|
26
|
+
export function redactEmails(value) {
|
|
27
|
+
return value.replace(/[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/g, (match) => maskEmail(match));
|
|
28
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rotation-storage-meta.d.ts","sourceRoot":"","sources":["../../../lib/runtime/rotation-storage-meta.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAqCnE,MAAM,WAAW,WAAW;IAC3B,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,kBAAkB,EAAE,MAAM,CAAC;CAC3B;AA8BD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,uBAAuB,CACtC,WAAW,GAAE,MAAyB,GACpC,WAAW,
|
|
1
|
+
{"version":3,"file":"rotation-storage-meta.d.ts","sourceRoot":"","sources":["../../../lib/runtime/rotation-storage-meta.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAqCnE,MAAM,WAAW,WAAW;IAC3B,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,kBAAkB,EAAE,MAAM,CAAC;CAC3B;AA8BD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,uBAAuB,CACtC,WAAW,GAAE,MAAyB,GACpC,WAAW,CAsFb;AAED;;;GAGG;AACH,wBAAgB,8BAA8B,CAC7C,WAAW,GAAE,MAAyB,GACpC,MAAM,GAAG,IAAI,CAEf;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,IAAI,IAAI,CAE9C;AAED;;;;;;GAMG;AACH,wBAAgB,+BAA+B,CAC9C,oBAAoB,EAAE,oBAAoB,GAAG,IAAI,EACjD,sBAAsB,EAAE,MAAM,EAC9B,WAAW,GAAE,MAAyB,GACpC,MAAM,CAOR"}
|
|
@@ -90,7 +90,10 @@ export function readStorageMetaFromDisk(storagePath = getStoragePath()) {
|
|
|
90
90
|
: null;
|
|
91
91
|
const affinityGeneration = typeof parsed.affinityGeneration === "number" &&
|
|
92
92
|
Number.isFinite(parsed.affinityGeneration) &&
|
|
93
|
-
|
|
93
|
+
// Safe, not merely integral: see readAffinityGenerationFromDisk. Past
|
|
94
|
+
// 2^53 the CLI's bump stops advancing, so a proxy that accepted the
|
|
95
|
+
// value would never observe another switch.
|
|
96
|
+
Number.isSafeInteger(parsed.affinityGeneration) &&
|
|
94
97
|
parsed.affinityGeneration >= 0
|
|
95
98
|
? parsed.affinityGeneration
|
|
96
99
|
: 0;
|
|
@@ -8,6 +8,19 @@ export { chooseAccount } from "./runtime/rotation-account-selection.js";
|
|
|
8
8
|
export { maybeInvalidateAffinityFromDisk, readPinnedAccountIndexFromDisk, readStorageMetaFromDisk, resetPinCacheForTesting, } from "./runtime/rotation-storage-meta.js";
|
|
9
9
|
export type { StorageMeta } from "./runtime/rotation-storage-meta.js";
|
|
10
10
|
/** @internal Stable identity key for in-memory quota snapshots across reloads. */
|
|
11
|
+
/**
|
|
12
|
+
* Quota-scheduler key prefix identifying ONE account across every family and
|
|
13
|
+
* model key it owns.
|
|
14
|
+
*
|
|
15
|
+
* The trailing separator is part of the contract: without it the prefix
|
|
16
|
+
* `account:email:foo` also matches `account:email:foobar:codex`, so clearing
|
|
17
|
+
* one account's observations would silently clear a neighbour's.
|
|
18
|
+
*/
|
|
19
|
+
export declare function buildQuotaScheduleAccountPrefix(account: Pick<ManagedAccount, "accountId" | "email" | "refreshToken"> & {
|
|
20
|
+
/** Stable per-record discriminator retained across token/account-id updates. */
|
|
21
|
+
addedAt?: number;
|
|
22
|
+
recordId?: string;
|
|
23
|
+
}): string;
|
|
11
24
|
export declare function buildQuotaScheduleKey(account: Pick<ManagedAccount, "accountId" | "email" | "refreshToken"> & {
|
|
12
25
|
/** Stable per-record discriminator retained across token/account-id updates. */
|
|
13
26
|
addedAt?: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runtime-rotation-proxy.d.ts","sourceRoot":"","sources":["../../lib/runtime-rotation-proxy.ts"],"names":[],"mappings":"AAGA,OAAO,EAIN,KAAK,cAAc,EACnB,MAAM,eAAe,CAAC;AAkCvB,OAAO,EAAkB,KAAK,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAkDtE,OAAO,KAAK,EAKX,2BAA2B,EAC3B,0BAA0B,EAE1B,MAAM,oCAAoC,CAAC;AAe5C,YAAY,EACX,2BAA2B,EAC3B,0BAA0B,EAC1B,0BAA0B,GAC1B,MAAM,oCAAoC,CAAC;AAC5C,OAAO,EACN,+BAA+B,EAC/B,0BAA0B,GAC1B,MAAM,kCAAkC,CAAC;AAC1C,YAAY,EAAE,0BAA0B,EAAE,MAAM,kCAAkC,CAAC;AACnF,OAAO,EAAE,aAAa,EAAE,MAAM,yCAAyC,CAAC;AACxE,OAAO,EACN,+BAA+B,EAC/B,8BAA8B,EAC9B,uBAAuB,EACvB,uBAAuB,GACvB,MAAM,oCAAoC,CAAC;AAC5C,YAAY,EAAE,WAAW,EAAE,MAAM,oCAAoC,CAAC;AA8DtE,kFAAkF;AAClF,wBAAgB,qBAAqB,CACpC,OAAO,EAAE,IAAI,CAAC,cAAc,EAAE,WAAW,GAAG,OAAO,GAAG,cAAc,CAAC,GAAG;IACvE,gFAAgF;IAChF,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;CAClB,EACD,MAAM,EAAE,WAAW,EACnB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,GACnB,MAAM,
|
|
1
|
+
{"version":3,"file":"runtime-rotation-proxy.d.ts","sourceRoot":"","sources":["../../lib/runtime-rotation-proxy.ts"],"names":[],"mappings":"AAGA,OAAO,EAIN,KAAK,cAAc,EACnB,MAAM,eAAe,CAAC;AAkCvB,OAAO,EAAkB,KAAK,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAkDtE,OAAO,KAAK,EAKX,2BAA2B,EAC3B,0BAA0B,EAE1B,MAAM,oCAAoC,CAAC;AAe5C,YAAY,EACX,2BAA2B,EAC3B,0BAA0B,EAC1B,0BAA0B,GAC1B,MAAM,oCAAoC,CAAC;AAC5C,OAAO,EACN,+BAA+B,EAC/B,0BAA0B,GAC1B,MAAM,kCAAkC,CAAC;AAC1C,YAAY,EAAE,0BAA0B,EAAE,MAAM,kCAAkC,CAAC;AACnF,OAAO,EAAE,aAAa,EAAE,MAAM,yCAAyC,CAAC;AACxE,OAAO,EACN,+BAA+B,EAC/B,8BAA8B,EAC9B,uBAAuB,EACvB,uBAAuB,GACvB,MAAM,oCAAoC,CAAC;AAC5C,YAAY,EAAE,WAAW,EAAE,MAAM,oCAAoC,CAAC;AA8DtE,kFAAkF;AAClF;;;;;;;GAOG;AACH,wBAAgB,+BAA+B,CAC9C,OAAO,EAAE,IAAI,CAAC,cAAc,EAAE,WAAW,GAAG,OAAO,GAAG,cAAc,CAAC,GAAG;IACvE,gFAAgF;IAChF,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;CAClB,GACC,MAAM,CAmBR;AAED,wBAAgB,qBAAqB,CACpC,OAAO,EAAE,IAAI,CAAC,cAAc,EAAE,WAAW,GAAG,OAAO,GAAG,cAAc,CAAC,GAAG;IACvE,gFAAgF;IAChF,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;CAClB,EACD,MAAM,EAAE,WAAW,EACnB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,GACnB,MAAM,CAER;AA8gBD;;;;;;;;GAQG;AACH,wBAAgB,2BAA2B,CAC1C,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,GAAG,SAAS,GACvC,MAAM,GAAG,IAAI,CAUf;AAED,wBAAsB,yBAAyB,CAC9C,OAAO,EAAE,2BAA2B,GAClC,OAAO,CAAC,0BAA0B,CAAC,CAmLrC"}
|
|
@@ -80,7 +80,15 @@ const PINNED_PERMANENT_SKIP_REASONS = new Set([
|
|
|
80
80
|
]);
|
|
81
81
|
const DEFAULT_QUOTA_REMAINING_THRESHOLD = 10;
|
|
82
82
|
/** @internal Stable identity key for in-memory quota snapshots across reloads. */
|
|
83
|
-
|
|
83
|
+
/**
|
|
84
|
+
* Quota-scheduler key prefix identifying ONE account across every family and
|
|
85
|
+
* model key it owns.
|
|
86
|
+
*
|
|
87
|
+
* The trailing separator is part of the contract: without it the prefix
|
|
88
|
+
* `account:email:foo` also matches `account:email:foobar:codex`, so clearing
|
|
89
|
+
* one account's observations would silently clear a neighbour's.
|
|
90
|
+
*/
|
|
91
|
+
export function buildQuotaScheduleAccountPrefix(account) {
|
|
84
92
|
const emailKey = normalizeEmailKey(account.email);
|
|
85
93
|
const accountId = account.accountId?.trim();
|
|
86
94
|
const refreshToken = account.refreshToken?.trim() ?? "";
|
|
@@ -97,7 +105,10 @@ export function buildQuotaScheduleKey(account, family, model) {
|
|
|
97
105
|
: accountId
|
|
98
106
|
? `id:${accountId}${recordDiscriminator}`
|
|
99
107
|
: `refresh:${createHash("sha256").update(refreshToken).digest("hex")}${recordDiscriminator}`;
|
|
100
|
-
return `account:${accountIdentity}
|
|
108
|
+
return `account:${accountIdentity}:`;
|
|
109
|
+
}
|
|
110
|
+
export function buildQuotaScheduleKey(account, family, model) {
|
|
111
|
+
return `${buildQuotaScheduleAccountPrefix(account)}${model ?? family}`;
|
|
101
112
|
}
|
|
102
113
|
const DEFAULT_MAX_RUNTIME_ACCOUNT_ATTEMPTS = 4;
|
|
103
114
|
// This is a hard safety ceiling over every pinned selection pass, including
|
|
@@ -862,7 +873,37 @@ async function handleRequestInner(state, req, res, traceId) {
|
|
|
862
873
|
// otherwise glue an in-flight chat thread to the previously selected
|
|
863
874
|
// account. The proxy itself never bumps the generation, so its own
|
|
864
875
|
// debounced disk writes do not clear affinity. See issue #474.
|
|
865
|
-
|
|
876
|
+
/** Reconcile once before selection or recording a new upstream observation. */
|
|
877
|
+
const reconcileManualSelection = () => {
|
|
878
|
+
const meta = readStorageMetaFromDisk();
|
|
879
|
+
for (const manager of state.knownAccountManagers) {
|
|
880
|
+
manager.applyManualSelection(meta);
|
|
881
|
+
}
|
|
882
|
+
if (meta.affinityGeneration > state.lastObservedAffinityGeneration) {
|
|
883
|
+
const switchedAccount = meta.pinnedAccountIndex === null
|
|
884
|
+
? null
|
|
885
|
+
: accountManager.getAccountByIndex(meta.pinnedAccountIndex);
|
|
886
|
+
if (switchedAccount) {
|
|
887
|
+
state.preemptiveQuotaScheduler.clearByPrefix(buildQuotaScheduleAccountPrefix(switchedAccount));
|
|
888
|
+
}
|
|
889
|
+
else if (meta.pinnedAccountIndex !== null) {
|
|
890
|
+
// A pin we cannot resolve: this long-lived proxy re-reads only
|
|
891
|
+
// pin/gen, never the account list, so a `login` that appended an
|
|
892
|
+
// account before the `switch` leaves the new index out of range
|
|
893
|
+
// here. The generation only bumps on the NEXT user-initiated
|
|
894
|
+
// switch, so skipping the clear and advancing anyway would strand
|
|
895
|
+
// the pre-switch quota observation forever, which is the exact
|
|
896
|
+
// deferral this reconciliation exists to end. Drop every cached
|
|
897
|
+
// observation instead: it only costs a re-probe, and real 429
|
|
898
|
+
// windows live on the accounts themselves, not in this cache.
|
|
899
|
+
state.preemptiveQuotaScheduler.clearAll();
|
|
900
|
+
}
|
|
901
|
+
state.sessionAffinityStore?.clearAll();
|
|
902
|
+
state.lastObservedAffinityGeneration = meta.affinityGeneration;
|
|
903
|
+
}
|
|
904
|
+
return meta;
|
|
905
|
+
};
|
|
906
|
+
const storageMeta = reconcileManualSelection();
|
|
866
907
|
// The ephemeral --account pin (issue #623) takes precedence over the
|
|
867
908
|
// persisted `switch` pin for this invocation, without ever mutating disk
|
|
868
909
|
// state. Use `??` (not `||`) so a forced index of 0 is honored. When set,
|
|
@@ -899,10 +940,6 @@ async function handleRequestInner(state, req, res, traceId) {
|
|
|
899
940
|
if (isPinned) {
|
|
900
941
|
transientAttemptLimit = Math.max(1, Math.min(state.maxRuntimeAccountAttempts, MAX_PINNED_TRANSIENT_ATTEMPTS));
|
|
901
942
|
}
|
|
902
|
-
if (storageMeta.affinityGeneration > state.lastObservedAffinityGeneration) {
|
|
903
|
-
state.sessionAffinityStore?.clearAll();
|
|
904
|
-
state.lastObservedAffinityGeneration = storageMeta.affinityGeneration;
|
|
905
|
-
}
|
|
906
943
|
let runtimeSelectionIterations = 0;
|
|
907
944
|
while ((isPinned || attemptedIndexes.size < accountCount) &&
|
|
908
945
|
transientAttempts < transientAttemptLimit &&
|
|
@@ -1153,6 +1190,7 @@ async function handleRequestInner(state, req, res, traceId) {
|
|
|
1153
1190
|
noteRotation();
|
|
1154
1191
|
continue;
|
|
1155
1192
|
}
|
|
1193
|
+
reconcileManualSelection();
|
|
1156
1194
|
const quotaSnapshot = readQuotaSchedulerSnapshot(upstream.headers, upstream.status, state.now());
|
|
1157
1195
|
if (quotaSnapshot) {
|
|
1158
1196
|
state.preemptiveQuotaScheduler.update(quotaScheduleKey, quotaSnapshot);
|
|
@@ -1162,6 +1200,8 @@ async function handleRequestInner(state, req, res, traceId) {
|
|
|
1162
1200
|
const retryAfterMs = parseRetryAfterHeaderMs(upstream.headers, state.now()) ??
|
|
1163
1201
|
parseRetryAfterBodyMs(bodyText, state.now()) ??
|
|
1164
1202
|
60_000;
|
|
1203
|
+
// Reading the body awaited I/O; a switch may have landed meanwhile.
|
|
1204
|
+
reconcileManualSelection();
|
|
1165
1205
|
state.preemptiveQuotaScheduler.markRateLimited(quotaScheduleKey, retryAfterMs, state.now());
|
|
1166
1206
|
// A 429 is the upstream quota signal for the attempted account, so
|
|
1167
1207
|
// keep the consumed runtime token drained.
|
package/dist/lib/storage.d.ts
CHANGED
|
@@ -109,9 +109,27 @@ export declare function bumpStorageAffinityGeneration(storage: {
|
|
|
109
109
|
* persisting routine state (rate-limit hits, cooldowns, etc.) so a CLI
|
|
110
110
|
* `switch`/`unpin` that landed between proxy startup and the save is not
|
|
111
111
|
* clobbered. Returns `pinnedAccountIndex: undefined` and
|
|
112
|
-
* `affinityGeneration: 0` on any failure. See issue #474.
|
|
112
|
+
* `affinityGeneration: 0` on any failure by default. See issue #474.
|
|
113
|
+
*
|
|
114
|
+
* `strict` callers additionally need to know when the metadata could not be
|
|
115
|
+
* OBSERVED AT ALL, because persisting a stale in-memory pin over a newer disk
|
|
116
|
+
* pin is exactly the #474 regression. Strict mode therefore throws for the two
|
|
117
|
+
* cases where a newer selection may be hiding behind the failure:
|
|
118
|
+
* - the file exists but cannot be read (EBUSY/EPERM/EACCES: on Windows an AV
|
|
119
|
+
* scanner or indexer can hold accounts.json open), and
|
|
120
|
+
* - the bytes are not valid JSON (a torn read of a concurrent atomic write).
|
|
121
|
+
*
|
|
122
|
+
* A MISSING file and a successfully parsed file with INVALID field values are
|
|
123
|
+
* deliberately not failures, in either mode. Neither can be concealing a newer
|
|
124
|
+
* pin (there is no file, or we can see the whole file and the field is
|
|
125
|
+
* unusable), and both are self-repaired by the very write that follows. Making
|
|
126
|
+
* them throw would wedge persistence permanently, since recreating or repairing
|
|
127
|
+
* storage goes through the same save path, and it would take every rate-limit
|
|
128
|
+
* window, cooldown and rotated refresh token in memory down with it.
|
|
113
129
|
*/
|
|
114
|
-
export declare function readPinAndGenFromDisk(path: string
|
|
130
|
+
export declare function readPinAndGenFromDisk(path: string, options?: {
|
|
131
|
+
strict?: boolean;
|
|
132
|
+
}): {
|
|
115
133
|
pinnedAccountIndex: number | undefined;
|
|
116
134
|
affinityGeneration: number;
|
|
117
135
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"storage.d.ts","sourceRoot":"","sources":["../../lib/storage.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"storage.d.ts","sourceRoot":"","sources":["../../lib/storage.ts"],"names":[],"mappings":"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"}
|