codex-multi-auth 2.6.1 → 2.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.codex-plugin/plugin.json +9 -9
- package/README.md +16 -8
- package/dist/lib/cli.d.ts +2 -0
- package/dist/lib/cli.d.ts.map +1 -1
- package/dist/lib/codex-manager/formatters/quota-formatters.d.ts +20 -3
- package/dist/lib/codex-manager/formatters/quota-formatters.d.ts.map +1 -1
- package/dist/lib/codex-manager/formatters/quota-formatters.js +28 -13
- package/dist/lib/codex-manager/help.d.ts.map +1 -1
- package/dist/lib/codex-manager/help.js +18 -14
- package/dist/lib/codex-manager/login-menu-actions.d.ts.map +1 -1
- package/dist/lib/codex-manager/login-menu-actions.js +12 -1
- package/dist/lib/codex-manager/login-menu-data.d.ts.map +1 -1
- package/dist/lib/codex-manager/login-menu-data.js +19 -8
- package/dist/lib/codex-manager.d.ts.map +1 -1
- package/dist/lib/codex-manager.js +13 -0
- package/dist/lib/forecast.d.ts.map +1 -1
- package/dist/lib/forecast.js +30 -4
- package/dist/lib/policy/runtime-policy.d.ts.map +1 -1
- package/dist/lib/policy/runtime-policy.js +12 -3
- package/dist/lib/quota-probe.d.ts +13 -0
- package/dist/lib/quota-probe.d.ts.map +1 -1
- package/dist/lib/quota-probe.js +6 -4
- package/dist/lib/quota-readiness.d.ts +1 -0
- package/dist/lib/quota-readiness.d.ts.map +1 -1
- package/dist/lib/quota-readiness.js +12 -2
- package/dist/lib/refresh-lease.d.ts +12 -0
- package/dist/lib/refresh-lease.d.ts.map +1 -1
- package/dist/lib/refresh-lease.js +24 -2
- package/dist/lib/refresh-queue.d.ts.map +1 -1
- package/dist/lib/refresh-queue.js +29 -3
- package/dist/lib/request/request-transformer.d.ts.map +1 -1
- package/dist/lib/request/request-transformer.js +13 -2
- package/dist/lib/request/response-handler.d.ts.map +1 -1
- package/dist/lib/request/response-handler.js +10 -1
- package/dist/lib/rotation.d.ts.map +1 -1
- package/dist/lib/rotation.js +8 -1
- package/dist/lib/runtime/account-check.d.ts +1 -1
- package/dist/lib/runtime/account-check.d.ts.map +1 -1
- package/dist/lib/runtime/account-check.js +8 -0
- package/dist/lib/runtime/rotation-storage-meta.d.ts.map +1 -1
- package/dist/lib/runtime/rotation-storage-meta.js +8 -2
- package/dist/lib/storage/account-port.d.ts +2 -0
- package/dist/lib/storage/account-port.d.ts.map +1 -1
- package/dist/lib/storage/account-port.js +1 -0
- package/dist/lib/storage/import-export.d.ts +1 -0
- package/dist/lib/storage/import-export.d.ts.map +1 -1
- package/dist/lib/storage/import-export.js +25 -0
- package/dist/lib/storage/project-migration.d.ts +1 -1
- package/dist/lib/storage/project-migration.d.ts.map +1 -1
- package/dist/lib/storage/project-migration.js +28 -2
- package/dist/lib/storage.d.ts +10 -0
- package/dist/lib/storage.d.ts.map +1 -1
- package/dist/lib/storage.js +16 -1
- package/dist/lib/ui/auth-menu-builder.d.ts +10 -0
- package/dist/lib/ui/auth-menu-builder.d.ts.map +1 -1
- package/dist/lib/ui/auth-menu-builder.js +29 -4
- package/package.json +1 -1
package/dist/lib/quota-probe.js
CHANGED
|
@@ -228,16 +228,18 @@ function formatQuotaWindowLabel(windowMinutes) {
|
|
|
228
228
|
* and produces no sensitive token data.
|
|
229
229
|
*
|
|
230
230
|
* @param resetAtMs - Timestamp in milliseconds since epoch; if `undefined`, non-finite, or <= 0, the function returns `undefined`.
|
|
231
|
+
* @param nowMs - Reference timestamp used for the "is today" comparison; defaults to the current time.
|
|
231
232
|
* @returns The formatted reset time string, or `undefined` if `resetAtMs` is invalid or not provided.
|
|
232
233
|
*/
|
|
233
|
-
function
|
|
234
|
+
export function formatQuotaResetAt(resetAtMs, nowMs = Date.now()) {
|
|
234
235
|
if (!resetAtMs || !Number.isFinite(resetAtMs) || resetAtMs <= 0)
|
|
235
236
|
return undefined;
|
|
236
237
|
const date = new Date(resetAtMs);
|
|
237
238
|
if (!Number.isFinite(date.getTime()))
|
|
238
239
|
return undefined;
|
|
239
|
-
const now = new Date();
|
|
240
|
-
const sameDay =
|
|
240
|
+
const now = new Date(nowMs);
|
|
241
|
+
const sameDay = Number.isFinite(now.getTime()) &&
|
|
242
|
+
now.getFullYear() === date.getFullYear() &&
|
|
241
243
|
now.getMonth() === date.getMonth() &&
|
|
242
244
|
now.getDate() === date.getDate();
|
|
243
245
|
const time = date.toLocaleTimeString(undefined, {
|
|
@@ -264,7 +266,7 @@ function formatWindowSummary(label, window) {
|
|
|
264
266
|
const left = typeof used === "number" && Number.isFinite(used)
|
|
265
267
|
? Math.max(0, Math.min(100, Math.round(100 - used)))
|
|
266
268
|
: undefined;
|
|
267
|
-
const reset =
|
|
269
|
+
const reset = formatQuotaResetAt(window.resetAtMs);
|
|
268
270
|
let summary = label;
|
|
269
271
|
if (left !== undefined)
|
|
270
272
|
summary = `${summary} ${left}% left`;
|
|
@@ -11,6 +11,7 @@ export type QuotaEmailFallbackState = {
|
|
|
11
11
|
export declare function buildQuotaEmailFallbackState(accounts: readonly QuotaCacheAccountRef[]): ReadonlyMap<string, QuotaEmailFallbackState>;
|
|
12
12
|
export declare function hasSafeQuotaEmailFallback(emailFallbackState: ReadonlyMap<string, QuotaEmailFallbackState>, account: QuotaCacheAccountRef): boolean;
|
|
13
13
|
export declare function quotaLeftPercentFromUsed(usedPercent: number | undefined): number | undefined;
|
|
14
|
+
export declare function quotaUsedPercentIsExhausted(usedPercent: number | undefined): boolean;
|
|
14
15
|
export declare function isQuotaCacheEntryExhausted(entry: Pick<QuotaCacheEntry, "primary" | "secondary"> & {
|
|
15
16
|
updatedAt?: number;
|
|
16
17
|
} | null | undefined, now?: number): boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"quota-readiness.d.ts","sourceRoot":"","sources":["../../lib/quota-readiness.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAoB,MAAM,kBAAkB,CAAC;AAC1F,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAEtD,MAAM,MAAM,oBAAoB,GAAG,IAAI,CAAC,iBAAiB,EAAE,WAAW,GAAG,OAAO,CAAC,CAAC;AAIlF,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAGhF;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAG5E;AAED,wBAAgB,uBAAuB,CACtC,QAAQ,EAAE,SAAS,oBAAoB,EAAE,EACzC,OAAO,EAAE,oBAAoB,GAC3B,OAAO,CAQT;AAED,MAAM,MAAM,uBAAuB,GAAG;IACrC,aAAa,EAAE,MAAM,CAAC;IACtB,kBAAkB,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;CAChC,CAAC;AAEF,wBAAgB,4BAA4B,CAC3C,QAAQ,EAAE,SAAS,oBAAoB,EAAE,GACvC,WAAW,CAAC,MAAM,EAAE,uBAAuB,CAAC,CAiB9C;AAED,wBAAgB,yBAAyB,CACxC,kBAAkB,EAAE,WAAW,CAAC,MAAM,EAAE,uBAAuB,CAAC,EAChE,OAAO,EAAE,oBAAoB,GAC3B,OAAO,CAMT;AAED,wBAAgB,wBAAwB,CACvC,WAAW,EAAE,MAAM,GAAG,SAAS,GAC7B,MAAM,GAAG,SAAS,CAKpB;
|
|
1
|
+
{"version":3,"file":"quota-readiness.d.ts","sourceRoot":"","sources":["../../lib/quota-readiness.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAoB,MAAM,kBAAkB,CAAC;AAC1F,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAEtD,MAAM,MAAM,oBAAoB,GAAG,IAAI,CAAC,iBAAiB,EAAE,WAAW,GAAG,OAAO,CAAC,CAAC;AAIlF,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAGhF;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAG5E;AAED,wBAAgB,uBAAuB,CACtC,QAAQ,EAAE,SAAS,oBAAoB,EAAE,EACzC,OAAO,EAAE,oBAAoB,GAC3B,OAAO,CAQT;AAED,MAAM,MAAM,uBAAuB,GAAG;IACrC,aAAa,EAAE,MAAM,CAAC;IACtB,kBAAkB,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;CAChC,CAAC;AAEF,wBAAgB,4BAA4B,CAC3C,QAAQ,EAAE,SAAS,oBAAoB,EAAE,GACvC,WAAW,CAAC,MAAM,EAAE,uBAAuB,CAAC,CAiB9C;AAED,wBAAgB,yBAAyB,CACxC,kBAAkB,EAAE,WAAW,CAAC,MAAM,EAAE,uBAAuB,CAAC,EAChE,OAAO,EAAE,oBAAoB,GAC3B,OAAO,CAMT;AAED,wBAAgB,wBAAwB,CACvC,WAAW,EAAE,MAAM,GAAG,SAAS,GAC7B,MAAM,GAAG,SAAS,CAKpB;AAQD,wBAAgB,2BAA2B,CAC1C,WAAW,EAAE,MAAM,GAAG,SAAS,GAC7B,OAAO,CAMT;AA2BD,wBAAgB,0BAA0B,CACzC,KAAK,EAAE,IAAI,CAAC,eAAe,EAAE,SAAS,GAAG,WAAW,CAAC,GAAG;IAAE,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,GAAG,SAAS,EACjG,GAAG,SAAa,GACd,OAAO,CAQT;AAED,wBAAgB,6BAA6B,CAC5C,KAAK,EAAE,cAAc,GAAG,IAAI,GAAG,SAAS,EACxC,OAAO,EAAE,oBAAoB,EAC7B,QAAQ,EAAE,SAAS,oBAAoB,EAAE,EACzC,kBAAkB,+CAAyC,GACzD,eAAe,GAAG,IAAI,CAWxB"}
|
|
@@ -53,6 +53,17 @@ export function quotaLeftPercentFromUsed(usedPercent) {
|
|
|
53
53
|
}
|
|
54
54
|
return Math.max(0, Math.min(100, Math.round(100 - usedPercent)));
|
|
55
55
|
}
|
|
56
|
+
// A window is exhausted only when it is genuinely at/over 100% used. Base this on
|
|
57
|
+
// the RAW usedPercent, never the rounded quotaLeftPercentFromUsed: rounding
|
|
58
|
+
// 100 - 99.6 = 0.4 down to 0 left-percent would falsely bench a window that still
|
|
59
|
+
// has ~0.4% quota (any usedPercent in (99.5, 100) rounds to 0 left). The header
|
|
60
|
+
// parser preserves fractional used-percent, so this input is expected;
|
|
61
|
+
// quotaLeftPercentFromUsed stays for DISPLAY only.
|
|
62
|
+
export function quotaUsedPercentIsExhausted(usedPercent) {
|
|
63
|
+
return (typeof usedPercent === "number" &&
|
|
64
|
+
Number.isFinite(usedPercent) &&
|
|
65
|
+
usedPercent >= 100);
|
|
66
|
+
}
|
|
56
67
|
function quotaWindowIsExhausted(window, now = Date.now(), updatedAt) {
|
|
57
68
|
if (typeof window?.resetAtMs === "number" && now >= window.resetAtMs) {
|
|
58
69
|
return false;
|
|
@@ -69,8 +80,7 @@ function quotaWindowIsExhausted(window, now = Date.now(), updatedAt) {
|
|
|
69
80
|
now >= updatedAt + window.windowMinutes * 60_000) {
|
|
70
81
|
return false;
|
|
71
82
|
}
|
|
72
|
-
|
|
73
|
-
return typeof leftPercent === "number" && leftPercent <= 0;
|
|
83
|
+
return quotaUsedPercentIsExhausted(window?.usedPercent);
|
|
74
84
|
}
|
|
75
85
|
export function isQuotaCacheEntryExhausted(entry, now = Date.now()) {
|
|
76
86
|
// Codex quota windows are cumulative gates: a 0% remaining active window blocks use
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { promises as fs } from "node:fs";
|
|
2
2
|
import type { TokenResult } from "./types.js";
|
|
3
|
+
export declare const DEFAULT_WAIT_TIMEOUT_MS = 35000;
|
|
3
4
|
type LeaseFsOps = Pick<typeof fs, "mkdir" | "open" | "writeFile" | "rename" | "unlink" | "readFile" | "stat" | "readdir"> & Partial<Pick<typeof fs, "chmod">>;
|
|
4
5
|
export interface RefreshLeaseCoordinatorOptions {
|
|
5
6
|
enabled?: boolean;
|
|
@@ -24,6 +25,17 @@ export declare class RefreshLeaseCoordinator {
|
|
|
24
25
|
private readonly resultTtlMs;
|
|
25
26
|
private readonly fsOps;
|
|
26
27
|
constructor(options?: RefreshLeaseCoordinatorOptions);
|
|
28
|
+
/**
|
|
29
|
+
* Resolved maximum time `acquire()` may block waiting for a lease.
|
|
30
|
+
*
|
|
31
|
+
* Exposed so the refresh queue can size its acquire-stage eviction threshold
|
|
32
|
+
* against the budget this coordinator ACTUALLY uses. The budget is
|
|
33
|
+
* configurable (constructor option / `CODEX_AUTH_REFRESH_LEASE_WAIT_MS`), so
|
|
34
|
+
* sizing eviction off the static `DEFAULT_WAIT_TIMEOUT_MS` would evict an
|
|
35
|
+
* acquire that is still legitimately waiting under a larger budget, spawning
|
|
36
|
+
* the duplicate refresh (→ `invalid_grant`) the lease exists to prevent.
|
|
37
|
+
*/
|
|
38
|
+
get configuredWaitTimeoutMs(): number;
|
|
27
39
|
static fromEnvironment(): RefreshLeaseCoordinator;
|
|
28
40
|
acquire(refreshToken: string): Promise<RefreshLeaseHandle>;
|
|
29
41
|
private createBypassHandle;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"refresh-lease.d.ts","sourceRoot":"","sources":["../../lib/refresh-lease.ts"],"names":[],"mappings":"AACA,OAAO,EAAc,QAAQ,IAAI,EAAE,EAAE,MAAM,SAAS,CAAC;AAMrD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"refresh-lease.d.ts","sourceRoot":"","sources":["../../lib/refresh-lease.ts"],"names":[],"mappings":"AACA,OAAO,EAAc,QAAQ,IAAI,EAAE,EAAE,MAAM,SAAS,CAAC;AAMrD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAQ9C,eAAO,MAAM,uBAAuB,QAAS,CAAC;AAsB9C,KAAK,UAAU,GAAG,IAAI,CACrB,OAAO,EAAE,EACT,OAAO,GAAG,MAAM,GAAG,WAAW,GAAG,QAAQ,GAAG,QAAQ,GAAG,UAAU,GAAG,MAAM,GAAG,SAAS,CACtF,GACA,OAAO,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,OAAO,CAAC,CAAC,CAAC;AAEnC,MAAM,WAAW,8BAA8B;IAC9C,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,UAAU,CAAC;CACnB;AAED,MAAM,WAAW,kBAAkB;IAClC,IAAI,EAAE,OAAO,GAAG,UAAU,GAAG,QAAQ,CAAC;IACtC,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,OAAO,EAAE,CAAC,MAAM,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACjD;AAyGD,qBAAa,uBAAuB;IACnC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAU;IAClC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IACvC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAS;IACxC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAa;gBAEvB,OAAO,GAAE,8BAAmC;IAUxD;;;;;;;;;OASG;IACH,IAAI,uBAAuB,IAAI,MAAM,CAEpC;IAED,MAAM,CAAC,eAAe,IAAI,uBAAuB;IAgB3C,OAAO,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,CAAC;IA+FhE,OAAO,CAAC,kBAAkB;IAU1B,OAAO,CAAC,iBAAiB;YA0CX,QAAQ;YAkBR,WAAW;YAwBX,eAAe;YAiBf,mBAAmB;YAuCnB,qBAAqB;CAsBnC"}
|
|
@@ -8,7 +8,9 @@ import { safeParseTokenResult } from "./schemas.js";
|
|
|
8
8
|
import { isRecord } from "./utils.js";
|
|
9
9
|
const log = createLogger("refresh-lease");
|
|
10
10
|
const DEFAULT_LEASE_TTL_MS = 30_000;
|
|
11
|
-
|
|
11
|
+
// Exported so the refresh queue can size its acquire-stage eviction threshold
|
|
12
|
+
// above the maximum time a lease acquire() may legitimately block waiting.
|
|
13
|
+
export const DEFAULT_WAIT_TIMEOUT_MS = 35_000;
|
|
12
14
|
const DEFAULT_POLL_INTERVAL_MS = 150;
|
|
13
15
|
const DEFAULT_RESULT_TTL_MS = 20_000;
|
|
14
16
|
const RETRYABLE_IO_ERRORS = new Set(["EBUSY", "EPERM", "EMFILE", "ENFILE"]);
|
|
@@ -120,6 +122,19 @@ export class RefreshLeaseCoordinator {
|
|
|
120
122
|
this.resultTtlMs = Math.max(1_000, Math.floor(options.resultTtlMs ?? DEFAULT_RESULT_TTL_MS));
|
|
121
123
|
this.fsOps = options.fsOps ?? fs;
|
|
122
124
|
}
|
|
125
|
+
/**
|
|
126
|
+
* Resolved maximum time `acquire()` may block waiting for a lease.
|
|
127
|
+
*
|
|
128
|
+
* Exposed so the refresh queue can size its acquire-stage eviction threshold
|
|
129
|
+
* against the budget this coordinator ACTUALLY uses. The budget is
|
|
130
|
+
* configurable (constructor option / `CODEX_AUTH_REFRESH_LEASE_WAIT_MS`), so
|
|
131
|
+
* sizing eviction off the static `DEFAULT_WAIT_TIMEOUT_MS` would evict an
|
|
132
|
+
* acquire that is still legitimately waiting under a larger budget, spawning
|
|
133
|
+
* the duplicate refresh (→ `invalid_grant`) the lease exists to prevent.
|
|
134
|
+
*/
|
|
135
|
+
get configuredWaitTimeoutMs() {
|
|
136
|
+
return this.waitTimeoutMs;
|
|
137
|
+
}
|
|
123
138
|
static fromEnvironment() {
|
|
124
139
|
const testMode = process.env.VITEST === "true" || process.env.NODE_ENV === "test";
|
|
125
140
|
const enabled = parseBooleanEnv(process.env.CODEX_AUTH_REFRESH_LEASE) ??
|
|
@@ -243,7 +258,14 @@ export class RefreshLeaseCoordinator {
|
|
|
243
258
|
return;
|
|
244
259
|
released = true;
|
|
245
260
|
try {
|
|
246
|
-
|
|
261
|
+
// Only ever cache a SUCCESSFUL refresh in the cross-process lease.
|
|
262
|
+
// A `failed` result carries no token material to share; caching it
|
|
263
|
+
// would make readFreshResult serve the failure verbatim to every
|
|
264
|
+
// follower for the whole result TTL (DEFAULT_RESULT_TTL_MS), blocking
|
|
265
|
+
// a real refresh and even escalating to cooldowns. On failure we skip
|
|
266
|
+
// the cache and still unlink the lock in `finally`, so the next caller
|
|
267
|
+
// becomes owner and retries immediately.
|
|
268
|
+
if (result?.type === "success") {
|
|
247
269
|
await this.writeResult(resultPath, tokenHash, result);
|
|
248
270
|
}
|
|
249
271
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"refresh-queue.d.ts","sourceRoot":"","sources":["../../lib/refresh-queue.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C,OAAO,EAAE,uBAAuB,
|
|
1
|
+
{"version":3,"file":"refresh-queue.d.ts","sourceRoot":"","sources":["../../lib/refresh-queue.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C,OAAO,EAAE,uBAAuB,EAA2B,MAAM,oBAAoB,CAAC;AAqCtF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,OAAO,CAAwC;IACvD,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAA0B;IAC3D,OAAO,CAAC,cAAc,CAAK;IAE3B;;;;OAIG;IACH,OAAO,CAAC,gBAAgB,CAAkC;IAE1D;;OAEG;IACH,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IAEvC;;;OAGG;gBAED,aAAa,GAAE,MAAe,EAC9B,gBAAgB,GAAE,uBAAmE;IAMvF;;;;;;;;OAQG;IACG,OAAO,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;IAuHzD,OAAO,CAAC,iBAAiB;IASzB,OAAO,CAAC,sBAAsB;YAShB,kCAAkC;IAchD;;OAEG;YACW,cAAc;IAsE5B;;;OAGG;IACH,OAAO,CAAC,OAAO;IA8Cf;;;;OAIG;IACH,YAAY,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO;IAI3C;;;OAGG;IACH,IAAI,YAAY,IAAI,MAAM,CAEzB;IAED;;OAEG;IACH,KAAK,IAAI,IAAI;CAId;AAQD;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,aAAa,CAAC,EAAE,MAAM,GAAG,YAAY,CAKpE;AAED;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,IAAI,CAGxC;AAED;;;;GAIG;AACH,wBAAsB,aAAa,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAE9E"}
|
|
@@ -10,9 +10,16 @@
|
|
|
10
10
|
import { createHash } from "node:crypto";
|
|
11
11
|
import { refreshAccessToken } from "./auth/auth.js";
|
|
12
12
|
import { createLogger } from "./logger.js";
|
|
13
|
-
import { RefreshLeaseCoordinator } from "./refresh-lease.js";
|
|
13
|
+
import { RefreshLeaseCoordinator, DEFAULT_WAIT_TIMEOUT_MS } from "./refresh-lease.js";
|
|
14
14
|
import { isAbortError } from "./utils.js";
|
|
15
15
|
const log = createLogger("refresh-queue");
|
|
16
|
+
/**
|
|
17
|
+
* Extra headroom beyond the lease wait budget before an acquire-stage entry is
|
|
18
|
+
* treated as genuinely stuck. A lease acquire() polls on an interval and does
|
|
19
|
+
* filesystem work, so it can overshoot its wait deadline slightly; this slack
|
|
20
|
+
* keeps cleanup from evicting an acquire that is still legitimately waiting.
|
|
21
|
+
*/
|
|
22
|
+
const ACQUIRE_EVICTION_SLACK_MS = 5_000;
|
|
16
23
|
/**
|
|
17
24
|
* Non-reversible correlation fingerprint for a token, for logs.
|
|
18
25
|
*
|
|
@@ -311,11 +318,28 @@ export class RefreshQueue {
|
|
|
311
318
|
*/
|
|
312
319
|
cleanup() {
|
|
313
320
|
const now = Date.now();
|
|
321
|
+
// A lease acquire() can legitimately block up to the coordinator's wait
|
|
322
|
+
// budget. Evicting an acquire-stage entry before that budget elapses would
|
|
323
|
+
// spawn a duplicate refresh whose refresh token OpenAI has already rotated on
|
|
324
|
+
// first use (invalid_grant). So an acquire-stage entry is only evicted once it
|
|
325
|
+
// exceeds BOTH the configured max age AND the lease wait budget plus slack —
|
|
326
|
+
// never merely maxEntryAgeMs, which can be shorter than the wait budget.
|
|
327
|
+
//
|
|
328
|
+
// Read the budget the coordinator was actually CONFIGURED with (constructor
|
|
329
|
+
// option / CODEX_AUTH_REFRESH_LEASE_WAIT_MS) rather than the static default:
|
|
330
|
+
// under a larger configured budget the default would evict too early. An
|
|
331
|
+
// injected test double may not expose the getter, so fall back to the default.
|
|
332
|
+
const configuredWaitTimeoutMs = this.leaseCoordinator.configuredWaitTimeoutMs;
|
|
333
|
+
const leaseWaitTimeoutMs = typeof configuredWaitTimeoutMs === "number" &&
|
|
334
|
+
Number.isFinite(configuredWaitTimeoutMs)
|
|
335
|
+
? configuredWaitTimeoutMs
|
|
336
|
+
: DEFAULT_WAIT_TIMEOUT_MS;
|
|
337
|
+
const acquireEvictionAgeMs = Math.max(this.maxEntryAgeMs, leaseWaitTimeoutMs + ACQUIRE_EVICTION_SLACK_MS);
|
|
314
338
|
for (const [token, entry] of this.pending.entries()) {
|
|
315
339
|
const ageMs = now - entry.startedAt;
|
|
316
|
-
if (ageMs <= this.maxEntryAgeMs)
|
|
317
|
-
continue;
|
|
318
340
|
if (entry.stage === "acquire") {
|
|
341
|
+
if (ageMs <= acquireEvictionAgeMs)
|
|
342
|
+
continue;
|
|
319
343
|
log.warn("Evicting stale refresh entry during lease acquire stage", {
|
|
320
344
|
tokenSuffix: tokenFingerprint(token),
|
|
321
345
|
ageMs,
|
|
@@ -324,6 +348,8 @@ export class RefreshQueue {
|
|
|
324
348
|
this.cleanupRotationMapping(token);
|
|
325
349
|
continue;
|
|
326
350
|
}
|
|
351
|
+
if (ageMs <= this.maxEntryAgeMs)
|
|
352
|
+
continue;
|
|
327
353
|
if (!entry.staleWarningLogged) {
|
|
328
354
|
log.warn("Refresh entry exceeded stale warning threshold", {
|
|
329
355
|
tokenSuffix: tokenFingerprint(token),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"request-transformer.d.ts","sourceRoot":"","sources":["../../../lib/request/request-transformer.ts"],"names":[],"mappings":"AAoBA,OAAO,KAAK,EACX,aAAa,EACb,SAAS,EACT,eAAe,EACf,WAAW,EAEX,UAAU,EACV,MAAM,aAAa,CAAC;AAGrB,KAAK,mBAAmB,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAO/C,MAAM,WAAW,0BAA0B;IAC1C,IAAI,EAAE,WAAW,CAAC;IAClB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,mBAAmB,CAAC,EAAE,mBAAmB,CAAC;IAC1C,wBAAwB,CAAC,EAAE,MAAM,CAAC;IAClC,6BAA6B,CAAC,EAAE,OAAO,CAAC;IACxC,wBAAwB,CAAC,EAAE,OAAO,CAAC;CACnC;AAID,OAAO,EACN,kBAAkB,EAClB,uCAAuC,GACvC,MAAM,0BAA0B,CAAC;AAElC;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAEhE;AAkBD;;;;;;GAMG;AACH,wBAAgB,cAAc,CAC7B,SAAS,EAAE,MAAM,EACjB,UAAU,GAAE,UAAuC,GACjD,aAAa,CAqEf;AAED;;;GAGG;AACH,wBAAgB,wBAAwB,CACvC,UAAU,GAAE,UAAuC,GACjD,UAAU,CAUZ;AA4QD;;;;;;;;;;;GAWG;AACH,wBAAgB,kBAAkB,CACjC,SAAS,EAAE,MAAM,GAAG,SAAS,EAC7B,UAAU,GAAE,aAAkB,GAC5B,eAAe,CAuBjB;AA2DD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,WAAW,CAC1B,KAAK,EAAE,SAAS,EAAE,GAAG,SAAS,EAC9B,OAAO,CAAC,EAAE;IAAE,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAE,GAC9B,SAAS,EAAE,GAAG,SAAS,CAsBzB;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CACtC,KAAK,EAAE,SAAS,EAAE,GAAG,SAAS,EAC9B,QAAQ,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE;IAAE,oBAAoB,CAAC,EAAE,OAAO,CAAA;CAAE,GAC1C,SAAS,EAAE,GAAG,SAAS,
|
|
1
|
+
{"version":3,"file":"request-transformer.d.ts","sourceRoot":"","sources":["../../../lib/request/request-transformer.ts"],"names":[],"mappings":"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"}
|
|
@@ -487,7 +487,8 @@ export function trimInputForFastSession(input, maxItems, options) {
|
|
|
487
487
|
}
|
|
488
488
|
break;
|
|
489
489
|
}
|
|
490
|
-
|
|
490
|
+
const tailStart = Math.max(0, input.length - safeMax);
|
|
491
|
+
for (let i = tailStart; i < input.length; i++) {
|
|
491
492
|
if (excludedHeadIndexes.has(i))
|
|
492
493
|
continue;
|
|
493
494
|
keepIndexes.add(i);
|
|
@@ -499,7 +500,17 @@ export function trimInputForFastSession(input, maxItems, options) {
|
|
|
499
500
|
return input;
|
|
500
501
|
if (trimmed.length <= safeMax)
|
|
501
502
|
return trimmed;
|
|
502
|
-
|
|
503
|
+
// Kept head items are always the LOWEST kept indexes, so they occupy the first
|
|
504
|
+
// `keptHead` entries of `trimmed`. Reserve budget for exactly that many.
|
|
505
|
+
// Recounting the kept indexes below `tailStart` instead would miss a head
|
|
506
|
+
// instruction that ALSO falls inside the tail window (input.length only just
|
|
507
|
+
// over safeMax), and the tail slice would then drop it. The two slices cannot
|
|
508
|
+
// overlap here: keptHead + tailBudget === safeMax < trimmed.length.
|
|
509
|
+
const tailBudget = Math.max(1, safeMax - keptHead);
|
|
510
|
+
return [
|
|
511
|
+
...trimmed.slice(0, keptHead),
|
|
512
|
+
...trimmed.slice(trimmed.length - tailBudget),
|
|
513
|
+
];
|
|
503
514
|
}
|
|
504
515
|
function isTrivialLatestPrompt(text) {
|
|
505
516
|
const normalized = text.trim();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"response-handler.d.ts","sourceRoot":"","sources":["../../../lib/request/response-handler.ts"],"names":[],"mappings":"AAwwBA;;;;;GAKG;AACH,wBAAsB,gBAAgB,CACrC,QAAQ,EAAE,QAAQ,EAClB,OAAO,EAAE,OAAO,EAChB,OAAO,CAAC,EAAE;IACT,YAAY,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5C,oBAAoB,CAAC,EAAE,MAAM,CAAC;CAC9B,GACC,OAAO,CAAC,QAAQ,CAAC,CA2HnB;AA6DD;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAQ3D;AA2BD;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,
|
|
1
|
+
{"version":3,"file":"response-handler.d.ts","sourceRoot":"","sources":["../../../lib/request/response-handler.ts"],"names":[],"mappings":"AAwwBA;;;;;GAKG;AACH,wBAAsB,gBAAgB,CACrC,QAAQ,EAAE,QAAQ,EAClB,OAAO,EAAE,OAAO,EAChB,OAAO,CAAC,EAAE;IACT,YAAY,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5C,oBAAoB,CAAC,EAAE,MAAM,CAAC;CAC9B,GACC,OAAO,CAAC,QAAQ,CAAC,CA2HnB;AA6DD;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAQ3D;AA2BD;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CA4CtD;AAED,wBAAgB,uBAAuB,CACtC,QAAQ,EAAE,QAAQ,EAClB,OAAO,EAAE,OAAO,EAChB,YAAY,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,GACzC,QAAQ,CAiBV"}
|
|
@@ -787,7 +787,16 @@ export function isEmptyResponse(body) {
|
|
|
787
787
|
const obj = body;
|
|
788
788
|
if (Object.keys(obj).length === 0)
|
|
789
789
|
return true;
|
|
790
|
-
const hasOutput = "output" in obj &&
|
|
790
|
+
const hasOutput = "output" in obj &&
|
|
791
|
+
obj.output !== null &&
|
|
792
|
+
obj.output !== undefined &&
|
|
793
|
+
(Array.isArray(obj.output)
|
|
794
|
+
? obj.output.some((o) => o !== null &&
|
|
795
|
+
o !== undefined &&
|
|
796
|
+
(typeof o !== "object" || Object.keys(o).length > 0))
|
|
797
|
+
: typeof obj.output === "string"
|
|
798
|
+
? obj.output.trim() !== ""
|
|
799
|
+
: true);
|
|
791
800
|
const hasChoices = "choices" in obj &&
|
|
792
801
|
Array.isArray(obj.choices) &&
|
|
793
802
|
obj.choices.some((c) => c !== null &&
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rotation.d.ts","sourceRoot":"","sources":["../../lib/rotation.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAUH,MAAM,WAAW,iBAAiB;IACjC,yCAAyC;IACzC,YAAY,EAAE,MAAM,CAAC;IACrB,+CAA+C;IAC/C,cAAc,EAAE,MAAM,CAAC;IACvB,mDAAmD;IACnD,YAAY,EAAE,MAAM,CAAC;IACrB,2BAA2B;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,2BAA2B;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,8CAA8C;IAC9C,sBAAsB,EAAE,MAAM,CAAC;CAC/B;AAED,eAAO,MAAM,2BAA2B,EAAE,iBAOzC,CAAC;AAQF,KAAK,UAAU,GAAG,MAAM,GAAG,MAAM,CAAC;AAWlC;;;GAGG;AACH,qBAAa,kBAAkB;IAC9B,OAAO,CAAC,OAAO,CAAuC;IACtD,OAAO,CAAC,MAAM,CAAoB;gBAEtB,MAAM,GAAE,OAAO,CAAC,iBAAiB,CAAM;IAInD,OAAO,CAAC,MAAM;IAId,OAAO,CAAC,oBAAoB;IAO5B,QAAQ,CAAC,UAAU,EAAE,UAAU,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM;IAO3D,sBAAsB,CAAC,UAAU,EAAE,UAAU,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM;IAMzE,aAAa,CAAC,UAAU,EAAE,UAAU,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI;IAiB9D,eAAe,CAAC,UAAU,EAAE,UAAU,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI;IAiBhE,aAAa,CAAC,UAAU,EAAE,UAAU,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI;IAiB9D,KAAK,CAAC,UAAU,EAAE,UAAU,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI;IAKtD,KAAK,IAAI,IAAI;IAIb,yBAAyB,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI;IAanD;;;;OAIG;IACH,eAAe,CAAC,UAAU,EAAE,UAAU,GAAG,IAAI;CAW7C;AAMD,MAAM,WAAW,iBAAiB;IACjC,+BAA+B;IAC/B,SAAS,EAAE,MAAM,CAAC;IAClB,oCAAoC;IACpC,eAAe,EAAE,MAAM,CAAC;CACxB;AAED,eAAO,MAAM,2BAA2B,EAAE,iBAGzC,CAAC;
|
|
1
|
+
{"version":3,"file":"rotation.d.ts","sourceRoot":"","sources":["../../lib/rotation.ts"],"names":[],"mappings":"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"}
|
package/dist/lib/rotation.js
CHANGED
|
@@ -132,7 +132,14 @@ export const DEFAULT_TOKEN_BUCKET_CONFIG = {
|
|
|
132
132
|
maxTokens: 50,
|
|
133
133
|
tokensPerMinute: 6,
|
|
134
134
|
};
|
|
135
|
-
|
|
135
|
+
// Must cover the full request lifetime so a token consumed at request start can
|
|
136
|
+
// still be refunded when the request fails at the very end. The runtime proxy
|
|
137
|
+
// refunds on network error / upstream timeout, and the default fetch timeout is
|
|
138
|
+
// 60_000ms (config.ts fetchTimeoutMs) — measured AFTER token consumption and a
|
|
139
|
+
// token refresh. 90_000ms = that 60s timeout plus slack for the refresh and
|
|
140
|
+
// processing, so a genuinely timed-out request's token is reversed instead of
|
|
141
|
+
// leaking (gradual token-bucket starvation -> spurious token-exhausted skips).
|
|
142
|
+
const TOKEN_REFUND_WINDOW_MS = 90_000;
|
|
136
143
|
/**
|
|
137
144
|
* Client-side token bucket for rate limiting requests per account.
|
|
138
145
|
* Prevents sending requests to accounts that are likely to be rate-limited.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ModelFamily } from "../prompts/codex.js";
|
|
2
|
-
import type
|
|
2
|
+
import { type AccountStorageV3, type FlaggedAccountMetadataV1 } from "../storage.js";
|
|
3
3
|
import type { AccountIdSource, TokenResult } from "../types.js";
|
|
4
4
|
import type { AccountCheckWorkingState } from "./account-check-types.js";
|
|
5
5
|
import type { CodexQuotaSnapshot } from "./quota-headers.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"account-check.d.ts","sourceRoot":"","sources":["../../../lib/runtime/account-check.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AACvD,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"account-check.d.ts","sourceRoot":"","sources":["../../../lib/runtime/account-check.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { maskEmail } from "../logger.js";
|
|
2
2
|
import { CODEX_UNAVAILABLE_PROBE_NOTE } from "../quota-probe.js";
|
|
3
3
|
import { isCodexUnavailableError } from "../errors.js";
|
|
4
|
+
import { reconcilePinnedAccountIndex, } from "../storage.js";
|
|
4
5
|
export async function runRuntimeAccountCheck(deepProbe, deps) {
|
|
5
6
|
const loadedStorage = await deps.hydrateEmails(await deps.loadAccounts());
|
|
6
7
|
const workingStorage = loadedStorage
|
|
@@ -185,8 +186,15 @@ export async function runRuntimeAccountCheck(deepProbe, deps) {
|
|
|
185
186
|
}
|
|
186
187
|
}
|
|
187
188
|
if (state.removeFromActive.size > 0) {
|
|
189
|
+
// Follow the manual pin by identity across the removal: the filter can drop
|
|
190
|
+
// several accounts at once, so a raw index would point at the wrong account
|
|
191
|
+
// (or out of range, wedging the pool). See #474.
|
|
192
|
+
const pinnedAccount = typeof workingStorage.pinnedAccountIndex === "number"
|
|
193
|
+
? workingStorage.accounts[workingStorage.pinnedAccountIndex]
|
|
194
|
+
: undefined;
|
|
188
195
|
workingStorage.accounts = workingStorage.accounts.filter((account) => !state.removeFromActive.has(account.refreshToken));
|
|
189
196
|
deps.clampRuntimeActiveIndices(workingStorage, deps.MODEL_FAMILIES);
|
|
197
|
+
workingStorage.pinnedAccountIndex = reconcilePinnedAccountIndex(pinnedAccount, workingStorage.accounts);
|
|
190
198
|
state.storageChanged = true;
|
|
191
199
|
}
|
|
192
200
|
const persistAccountAndFlaggedStorage = deps.persistAccountAndFlaggedStorage;
|
|
@@ -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,CAmFb;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"}
|
|
@@ -78,9 +78,15 @@ export function readStorageMetaFromDisk(storagePath = getStoragePath()) {
|
|
|
78
78
|
return metaFromSnapshot(refreshed);
|
|
79
79
|
}
|
|
80
80
|
const parsed = JSON.parse(bytes.toString("utf8"));
|
|
81
|
+
// Validate exactly like readPinAndGenFromDisk / the loader / the zod schema:
|
|
82
|
+
// a non-integer or negative pin is rejected (null), never truncated. This is
|
|
83
|
+
// the value the proxy actually routes on, so a malformed disk pin must not
|
|
84
|
+
// slip past here and reach chooseAccount (a negative index wedges the pool).
|
|
81
85
|
const pinnedAccountIndex = typeof parsed.pinnedAccountIndex === "number" &&
|
|
82
|
-
Number.isFinite(parsed.pinnedAccountIndex)
|
|
83
|
-
|
|
86
|
+
Number.isFinite(parsed.pinnedAccountIndex) &&
|
|
87
|
+
Number.isInteger(parsed.pinnedAccountIndex) &&
|
|
88
|
+
parsed.pinnedAccountIndex >= 0
|
|
89
|
+
? parsed.pinnedAccountIndex
|
|
84
90
|
: null;
|
|
85
91
|
const affinityGeneration = typeof parsed.affinityGeneration === "number" &&
|
|
86
92
|
Number.isFinite(parsed.affinityGeneration) &&
|
|
@@ -33,6 +33,7 @@ export declare function importAccountsSnapshot(params: {
|
|
|
33
33
|
imported: AccountStorageV3;
|
|
34
34
|
maxAccounts: number;
|
|
35
35
|
deduplicateAccounts: (accounts: AccountStorageV3["accounts"]) => AccountStorageV3["accounts"];
|
|
36
|
+
findMatchingAccountIndex: (accounts: AccountStorageV3["accounts"], candidate: AccountStorageV3["accounts"][number]) => number | undefined;
|
|
36
37
|
}) => {
|
|
37
38
|
newStorage: AccountStorageV3;
|
|
38
39
|
imported: number;
|
|
@@ -41,6 +42,7 @@ export declare function importAccountsSnapshot(params: {
|
|
|
41
42
|
};
|
|
42
43
|
maxAccounts: number;
|
|
43
44
|
deduplicateAccounts: (accounts: AccountStorageV3["accounts"]) => AccountStorageV3["accounts"];
|
|
45
|
+
findMatchingAccountIndex: (accounts: AccountStorageV3["accounts"], candidate: AccountStorageV3["accounts"][number]) => number | undefined;
|
|
44
46
|
logInfo: (message: string, details: Record<string, unknown>) => void;
|
|
45
47
|
}): Promise<{
|
|
46
48
|
imported: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"account-port.d.ts","sourceRoot":"","sources":["../../../lib/storage/account-port.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAE1D,wBAAsB,sBAAsB,CAAC,MAAM,EAAE;IACpD,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,OAAO,CAAC;IACf,kBAAkB,EAAE,MAAM,CAAC;IAC3B,gBAAgB,EACb;QACA,MAAM,EAAE,OAAO,CAAC;QAChB,WAAW,EAAE,MAAM,CAAC;QACpB,QAAQ,EAAE,gBAAgB,GAAG,IAAI,CAAC;KACjC,GACD,SAAS,CAAC;IACb,0BAA0B,EAAE,MAAM,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAAC;IACnE,kBAAkB,EAAE,MAAM,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAAC;IAC3D,oBAAoB,EAAE,CAAC,IAAI,EAAE;QAC5B,YAAY,EAAE,MAAM,CAAC;QACrB,KAAK,EAAE,OAAO,CAAC;QACf,OAAO,EAAE,gBAAgB,GAAG,IAAI,CAAC;QACjC,YAAY,CAAC,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;QAC9D,OAAO,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;KACrE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACpB,YAAY,CAAC,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IAC9D,OAAO,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;CACrE,GAAG,OAAO,CAAC,IAAI,CAAC,CAgBhB;AAED,wBAAsB,sBAAsB,CAAC,MAAM,EAAE;IACpD,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,CAAC,IAAI,EAAE;QACtB,YAAY,EAAE,MAAM,CAAC;QACrB,uBAAuB,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,gBAAgB,GAAG,IAAI,CAAC;KACrE,KAAK,OAAO,CAAC,gBAAgB,CAAC,CAAC;IAChC,uBAAuB,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,gBAAgB,GAAG,IAAI,CAAC;IACrE,6BAA6B,EAAE,CAAC,CAAC,EAChC,OAAO,EAAE,CACR,OAAO,EAAE,gBAAgB,GAAG,IAAI,EAChC,OAAO,EAAE,CAAC,OAAO,EAAE,gBAAgB,KAAK,OAAO,CAAC,IAAI,CAAC,KACjD,OAAO,CAAC,CAAC,CAAC,KACX,OAAO,CAAC,CAAC,CAAC,CAAC;IAChB,qBAAqB,EAAE,CAAC,IAAI,EAAE;QAC7B,QAAQ,EAAE,gBAAgB,GAAG,IAAI,CAAC;QAClC,QAAQ,EAAE,gBAAgB,CAAC;QAC3B,WAAW,EAAE,MAAM,CAAC;QACpB,mBAAmB,EAAE,CACpB,QAAQ,EAAE,gBAAgB,CAAC,UAAU,CAAC,KAClC,gBAAgB,CAAC,UAAU,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"account-port.d.ts","sourceRoot":"","sources":["../../../lib/storage/account-port.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAE1D,wBAAsB,sBAAsB,CAAC,MAAM,EAAE;IACpD,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,OAAO,CAAC;IACf,kBAAkB,EAAE,MAAM,CAAC;IAC3B,gBAAgB,EACb;QACA,MAAM,EAAE,OAAO,CAAC;QAChB,WAAW,EAAE,MAAM,CAAC;QACpB,QAAQ,EAAE,gBAAgB,GAAG,IAAI,CAAC;KACjC,GACD,SAAS,CAAC;IACb,0BAA0B,EAAE,MAAM,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAAC;IACnE,kBAAkB,EAAE,MAAM,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAAC;IAC3D,oBAAoB,EAAE,CAAC,IAAI,EAAE;QAC5B,YAAY,EAAE,MAAM,CAAC;QACrB,KAAK,EAAE,OAAO,CAAC;QACf,OAAO,EAAE,gBAAgB,GAAG,IAAI,CAAC;QACjC,YAAY,CAAC,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;QAC9D,OAAO,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;KACrE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACpB,YAAY,CAAC,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IAC9D,OAAO,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;CACrE,GAAG,OAAO,CAAC,IAAI,CAAC,CAgBhB;AAED,wBAAsB,sBAAsB,CAAC,MAAM,EAAE;IACpD,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,CAAC,IAAI,EAAE;QACtB,YAAY,EAAE,MAAM,CAAC;QACrB,uBAAuB,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,gBAAgB,GAAG,IAAI,CAAC;KACrE,KAAK,OAAO,CAAC,gBAAgB,CAAC,CAAC;IAChC,uBAAuB,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,gBAAgB,GAAG,IAAI,CAAC;IACrE,6BAA6B,EAAE,CAAC,CAAC,EAChC,OAAO,EAAE,CACR,OAAO,EAAE,gBAAgB,GAAG,IAAI,EAChC,OAAO,EAAE,CAAC,OAAO,EAAE,gBAAgB,KAAK,OAAO,CAAC,IAAI,CAAC,KACjD,OAAO,CAAC,CAAC,CAAC,KACX,OAAO,CAAC,CAAC,CAAC,CAAC;IAChB,qBAAqB,EAAE,CAAC,IAAI,EAAE;QAC7B,QAAQ,EAAE,gBAAgB,GAAG,IAAI,CAAC;QAClC,QAAQ,EAAE,gBAAgB,CAAC;QAC3B,WAAW,EAAE,MAAM,CAAC;QACpB,mBAAmB,EAAE,CACpB,QAAQ,EAAE,gBAAgB,CAAC,UAAU,CAAC,KAClC,gBAAgB,CAAC,UAAU,CAAC,CAAC;QAClC,wBAAwB,EAAE,CACzB,QAAQ,EAAE,gBAAgB,CAAC,UAAU,CAAC,EACtC,SAAS,EAAE,gBAAgB,CAAC,UAAU,CAAC,CAAC,MAAM,CAAC,KAC3C,MAAM,GAAG,SAAS,CAAC;KACxB,KAAK;QACL,UAAU,EAAE,gBAAgB,CAAC;QAC7B,QAAQ,EAAE,MAAM,CAAC;QACjB,KAAK,EAAE,MAAM,CAAC;QACd,OAAO,EAAE,MAAM,CAAC;KAChB,CAAC;IACF,WAAW,EAAE,MAAM,CAAC;IACpB,mBAAmB,EAAE,CACpB,QAAQ,EAAE,gBAAgB,CAAC,UAAU,CAAC,KAClC,gBAAgB,CAAC,UAAU,CAAC,CAAC;IAGlC,wBAAwB,EAAE,CACzB,QAAQ,EAAE,gBAAgB,CAAC,UAAU,CAAC,EACtC,SAAS,EAAE,gBAAgB,CAAC,UAAU,CAAC,CAAC,MAAM,CAAC,KAC3C,MAAM,GAAG,SAAS,CAAC;IACxB,OAAO,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;CACrE,GAAG,OAAO,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC,CA+BhE"}
|
|
@@ -24,6 +24,7 @@ export async function importAccountsSnapshot(params) {
|
|
|
24
24
|
imported: normalized,
|
|
25
25
|
maxAccounts: params.maxAccounts,
|
|
26
26
|
deduplicateAccounts: params.deduplicateAccounts,
|
|
27
|
+
findMatchingAccountIndex: params.findMatchingAccountIndex,
|
|
27
28
|
});
|
|
28
29
|
await persist(merged.newStorage);
|
|
29
30
|
return {
|
|
@@ -15,6 +15,7 @@ export declare function mergeImportedAccounts(params: {
|
|
|
15
15
|
imported: AccountStorageV3;
|
|
16
16
|
maxAccounts: number;
|
|
17
17
|
deduplicateAccounts: (accounts: AccountStorageV3["accounts"]) => AccountStorageV3["accounts"];
|
|
18
|
+
findMatchingAccountIndex: (accounts: AccountStorageV3["accounts"], candidate: AccountStorageV3["accounts"][number]) => number | undefined;
|
|
18
19
|
}): {
|
|
19
20
|
newStorage: AccountStorageV3;
|
|
20
21
|
imported: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"import-export.d.ts","sourceRoot":"","sources":["../../../lib/storage/import-export.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAsD1D,wBAAsB,oBAAoB,CAAC,MAAM,EAAE;IAClD,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,OAAO,CAAC;IACf,OAAO,EAAE,gBAAgB,GAAG,IAAI,CAAC;IACjC,YAAY,CAAC,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IAC9D,OAAO,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;CACrE,GAAG,OAAO,CAAC,IAAI,CAAC,CAuChB;AAED,wBAAsB,cAAc,CAAC,MAAM,EAAE;IAC5C,YAAY,EAAE,MAAM,CAAC;IACrB,uBAAuB,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,gBAAgB,GAAG,IAAI,CAAC;CACrE,GAAG,OAAO,CAAC,gBAAgB,CAAC,CA8C5B;AAED,wBAAgB,qBAAqB,CAAC,MAAM,EAAE;IAC7C,QAAQ,EAAE,gBAAgB,GAAG,IAAI,CAAC;IAClC,QAAQ,EAAE,gBAAgB,CAAC;IAC3B,WAAW,EAAE,MAAM,CAAC;IACpB,mBAAmB,EAAE,CACpB,QAAQ,EAAE,gBAAgB,CAAC,UAAU,CAAC,KAClC,gBAAgB,CAAC,UAAU,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"import-export.d.ts","sourceRoot":"","sources":["../../../lib/storage/import-export.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAsD1D,wBAAsB,oBAAoB,CAAC,MAAM,EAAE;IAClD,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,OAAO,CAAC;IACf,OAAO,EAAE,gBAAgB,GAAG,IAAI,CAAC;IACjC,YAAY,CAAC,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IAC9D,OAAO,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;CACrE,GAAG,OAAO,CAAC,IAAI,CAAC,CAuChB;AAED,wBAAsB,cAAc,CAAC,MAAM,EAAE;IAC5C,YAAY,EAAE,MAAM,CAAC;IACrB,uBAAuB,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,gBAAgB,GAAG,IAAI,CAAC;CACrE,GAAG,OAAO,CAAC,gBAAgB,CAAC,CA8C5B;AAED,wBAAgB,qBAAqB,CAAC,MAAM,EAAE;IAC7C,QAAQ,EAAE,gBAAgB,GAAG,IAAI,CAAC;IAClC,QAAQ,EAAE,gBAAgB,CAAC;IAC3B,WAAW,EAAE,MAAM,CAAC;IACpB,mBAAmB,EAAE,CACpB,QAAQ,EAAE,gBAAgB,CAAC,UAAU,CAAC,KAClC,gBAAgB,CAAC,UAAU,CAAC,CAAC;IAGlC,wBAAwB,EAAE,CACzB,QAAQ,EAAE,gBAAgB,CAAC,UAAU,CAAC,EACtC,SAAS,EAAE,gBAAgB,CAAC,UAAU,CAAC,CAAC,MAAM,CAAC,KAC3C,MAAM,GAAG,SAAS,CAAC;CACxB,GAAG;IACH,UAAU,EAAE,gBAAgB,CAAC;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;CAChB,CA6DA"}
|
|
@@ -127,6 +127,14 @@ export async function readImportFile(params) {
|
|
|
127
127
|
export function mergeImportedAccounts(params) {
|
|
128
128
|
const existingAccounts = params.existing?.accounts ?? [];
|
|
129
129
|
const existingActiveIndex = params.existing?.activeIndex ?? 0;
|
|
130
|
+
// Resolve the pinned account by IDENTITY before merging. The merged list is
|
|
131
|
+
// deduplicated below, which can move accounts to different positions, so a raw
|
|
132
|
+
// positional pin can end up selecting a DIFFERENT account (in range, wrong
|
|
133
|
+
// account) instead of the one the user pinned.
|
|
134
|
+
const existingPinnedIndex = params.existing?.pinnedAccountIndex;
|
|
135
|
+
const pinnedAccount = typeof existingPinnedIndex === "number"
|
|
136
|
+
? existingAccounts[existingPinnedIndex]
|
|
137
|
+
: undefined;
|
|
130
138
|
const merged = [...existingAccounts, ...params.imported.accounts];
|
|
131
139
|
if (merged.length > params.maxAccounts) {
|
|
132
140
|
const deduped = params.deduplicateAccounts(merged);
|
|
@@ -142,6 +150,23 @@ export function mergeImportedAccounts(params) {
|
|
|
142
150
|
activeIndex: existingActiveIndex,
|
|
143
151
|
activeIndexByFamily: params.existing?.activeIndexByFamily,
|
|
144
152
|
};
|
|
153
|
+
// Preserve the user's manual pin (#474) and affinity generation across import.
|
|
154
|
+
// Rebuilding storage from scratch here previously dropped a `switch <n>` pin
|
|
155
|
+
// and reset affinityGeneration to 0, which lets a running proxy holding a
|
|
156
|
+
// higher in-memory generation clobber a newer CLI pin. The pin is re-resolved
|
|
157
|
+
// against the deduplicated list by identity — a range check alone would happily
|
|
158
|
+
// keep an in-range index that dedupe has repointed at another account. When the
|
|
159
|
+
// pinned account no longer resolves (removed or ambiguous) the pin is dropped
|
|
160
|
+
// rather than left pointing somewhere arbitrary.
|
|
161
|
+
if (pinnedAccount) {
|
|
162
|
+
const remappedPinnedIndex = params.findMatchingAccountIndex(deduplicatedAccounts, pinnedAccount);
|
|
163
|
+
if (remappedPinnedIndex !== undefined) {
|
|
164
|
+
newStorage.pinnedAccountIndex = remappedPinnedIndex;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
if (typeof params.existing?.affinityGeneration === "number") {
|
|
168
|
+
newStorage.affinityGeneration = params.existing.affinityGeneration;
|
|
169
|
+
}
|
|
145
170
|
const importedCount = deduplicatedAccounts.length - deduplicatedExistingAccounts.length;
|
|
146
171
|
const skippedCount = params.imported.accounts.length - importedCount;
|
|
147
172
|
return {
|
|
@@ -6,5 +6,5 @@ export declare function loadNormalizedStorageFromPath(path: string, label: strin
|
|
|
6
6
|
}>;
|
|
7
7
|
logWarn: (message: string, details: Record<string, unknown>) => void;
|
|
8
8
|
}): Promise<AccountStorageV3 | null>;
|
|
9
|
-
export declare function mergeStorageForMigration(current: AccountStorageV3 | null, incoming: AccountStorageV3, normalizeAccountStorage: (value: unknown) => AccountStorageV3 | null): AccountStorageV3;
|
|
9
|
+
export declare function mergeStorageForMigration(current: AccountStorageV3 | null, incoming: AccountStorageV3, normalizeAccountStorage: (value: unknown) => AccountStorageV3 | null, findMatchingAccountIndex: (accounts: AccountStorageV3["accounts"], candidate: AccountStorageV3["accounts"][number]) => number | undefined): AccountStorageV3;
|
|
10
10
|
//# sourceMappingURL=project-migration.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"project-migration.d.ts","sourceRoot":"","sources":["../../../lib/storage/project-migration.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAE1D,wBAAsB,6BAA6B,CAClD,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,EACb,IAAI,EAAE;IACL,oBAAoB,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;QAC/C,UAAU,EAAE,gBAAgB,GAAG,IAAI,CAAC;QACpC,YAAY,EAAE,MAAM,EAAE,CAAC;KACvB,CAAC,CAAC;IACH,OAAO,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;CACrE,GACC,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAoBlC;AAED,wBAAgB,wBAAwB,CACvC,OAAO,EAAE,gBAAgB,GAAG,IAAI,EAChC,QAAQ,EAAE,gBAAgB,EAC1B,uBAAuB,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,gBAAgB,GAAG,IAAI,
|
|
1
|
+
{"version":3,"file":"project-migration.d.ts","sourceRoot":"","sources":["../../../lib/storage/project-migration.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAE1D,wBAAsB,6BAA6B,CAClD,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,EACb,IAAI,EAAE;IACL,oBAAoB,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;QAC/C,UAAU,EAAE,gBAAgB,GAAG,IAAI,CAAC;QACpC,YAAY,EAAE,MAAM,EAAE,CAAC;KACvB,CAAC,CAAC;IACH,OAAO,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;CACrE,GACC,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAoBlC;AAED,wBAAgB,wBAAwB,CACvC,OAAO,EAAE,gBAAgB,GAAG,IAAI,EAChC,QAAQ,EAAE,gBAAgB,EAC1B,uBAAuB,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,gBAAgB,GAAG,IAAI,EAIpE,wBAAwB,EAAE,CACzB,QAAQ,EAAE,gBAAgB,CAAC,UAAU,CAAC,EACtC,SAAS,EAAE,gBAAgB,CAAC,UAAU,CAAC,CAAC,MAAM,CAAC,KAC3C,MAAM,GAAG,SAAS,GACrB,gBAAgB,CAuClB"}
|
|
@@ -20,18 +20,44 @@ export async function loadNormalizedStorageFromPath(path, label, deps) {
|
|
|
20
20
|
return null;
|
|
21
21
|
}
|
|
22
22
|
}
|
|
23
|
-
export function mergeStorageForMigration(current, incoming, normalizeAccountStorage
|
|
23
|
+
export function mergeStorageForMigration(current, incoming, normalizeAccountStorage,
|
|
24
|
+
// Injected like `normalizeAccountStorage` rather than imported from
|
|
25
|
+
// ../storage.js, which would put this leaf module in a dependency cycle with
|
|
26
|
+
// the storage barrel.
|
|
27
|
+
findMatchingAccountIndex) {
|
|
24
28
|
if (!current) {
|
|
25
29
|
return incoming;
|
|
26
30
|
}
|
|
31
|
+
// Resolve the pinned account by IDENTITY before merging. normalizeAccountStorage
|
|
32
|
+
// deduplicates the merged list but only RANGE-validates the pin, so a raw
|
|
33
|
+
// positional pin that survives the range check can still end up pointing at a
|
|
34
|
+
// different account once dedupe shifts positions.
|
|
35
|
+
const pinnedAccount = typeof current.pinnedAccountIndex === "number"
|
|
36
|
+
? current.accounts[current.pinnedAccountIndex]
|
|
37
|
+
: undefined;
|
|
27
38
|
const merged = normalizeAccountStorage({
|
|
28
39
|
version: 3,
|
|
29
40
|
activeIndex: current.activeIndex,
|
|
30
41
|
activeIndexByFamily: current.activeIndexByFamily,
|
|
31
42
|
accounts: [...current.accounts, ...incoming.accounts],
|
|
43
|
+
// Carry the manual pin (#474) and affinity generation from the current
|
|
44
|
+
// storage through legacy-project migration; normalizeAccountStorage
|
|
45
|
+
// validates/clamps them against the merged account list. Omitting them
|
|
46
|
+
// dropped the pin and reset affinityGeneration to 0, which let a running
|
|
47
|
+
// proxy clobber a newer CLI pin.
|
|
48
|
+
pinnedAccountIndex: current.pinnedAccountIndex,
|
|
49
|
+
affinityGeneration: current.affinityGeneration,
|
|
32
50
|
});
|
|
33
51
|
if (!merged) {
|
|
34
52
|
return current;
|
|
35
53
|
}
|
|
36
|
-
|
|
54
|
+
if (!pinnedAccount) {
|
|
55
|
+
return merged;
|
|
56
|
+
}
|
|
57
|
+
// Re-point the pin at the account the user actually pinned; clear it when that
|
|
58
|
+
// account no longer resolves in the normalized list.
|
|
59
|
+
return {
|
|
60
|
+
...merged,
|
|
61
|
+
pinnedAccountIndex: findMatchingAccountIndex(merged.accounts, pinnedAccount),
|
|
62
|
+
};
|
|
37
63
|
}
|
package/dist/lib/storage.d.ts
CHANGED
|
@@ -35,6 +35,16 @@ type AccountMatchOptions = {
|
|
|
35
35
|
allowUniqueAccountIdFallbackWithoutEmail?: boolean;
|
|
36
36
|
};
|
|
37
37
|
export declare function findMatchingAccountIndex<T extends Pick<AccountLike, "accountId" | "email" | "refreshToken">>(accounts: readonly T[], candidate: Pick<AccountLike, "accountId" | "email" | "refreshToken">, options?: AccountMatchOptions): number | undefined;
|
|
38
|
+
/**
|
|
39
|
+
* Re-resolve a manual pin after the account list changes (deletion / auto-removal).
|
|
40
|
+
* `pinnedAccountIndex` is a POSITION, so once accounts are removed or reordered the
|
|
41
|
+
* raw index silently points at a DIFFERENT account — or out of range, which wedges
|
|
42
|
+
* the pool. Capture the pinned account BEFORE the change and pass it here to follow
|
|
43
|
+
* it by identity: returns its new index, or `undefined` when it is no longer present
|
|
44
|
+
* (the pin is cleared, never left dangling at a stale slot). See #474 — a manual pin
|
|
45
|
+
* must keep pointing at the account the user chose, or be dropped.
|
|
46
|
+
*/
|
|
47
|
+
export declare function reconcilePinnedAccountIndex(pinnedAccount: Pick<AccountLike, "accountId" | "email" | "refreshToken"> | undefined, nextAccounts: readonly Pick<AccountLike, "accountId" | "email" | "refreshToken">[]): number | undefined;
|
|
38
48
|
export declare function resolveAccountSelectionIndex<T extends Pick<AccountLike, "accountId" | "email" | "refreshToken">>(accounts: readonly T[], candidate: Pick<AccountLike, "accountId" | "email" | "refreshToken">, fallbackIndex?: number): number;
|
|
39
49
|
/**
|
|
40
50
|
* Removes duplicate accounts, keeping the most recently used entry for each
|