codex-multi-auth 2.16.0 → 2.17.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 +22 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -3
- package/dist/lib/account-policy.d.ts +4 -0
- package/dist/lib/account-policy.d.ts.map +1 -1
- package/dist/lib/account-policy.js +8 -0
- package/dist/lib/accounts/rate-limits.d.ts.map +1 -1
- package/dist/lib/accounts/rate-limits.js +2 -0
- package/dist/lib/accounts.d.ts +21 -3
- package/dist/lib/accounts.d.ts.map +1 -1
- package/dist/lib/accounts.js +229 -17
- package/dist/lib/api-route-store.d.ts +24 -0
- package/dist/lib/api-route-store.d.ts.map +1 -0
- package/dist/lib/api-route-store.js +145 -0
- package/dist/lib/auth/account-access.d.ts +115 -0
- package/dist/lib/auth/account-access.d.ts.map +1 -0
- package/dist/lib/auth/account-access.js +214 -0
- package/dist/lib/auth/token-utils.d.ts +54 -1
- package/dist/lib/auth/token-utils.d.ts.map +1 -1
- package/dist/lib/auth/token-utils.js +92 -7
- package/dist/lib/cli.d.ts +1 -1
- package/dist/lib/cli.d.ts.map +1 -1
- package/dist/lib/cli.js +4 -0
- package/dist/lib/codex-cli/state.d.ts +7 -0
- package/dist/lib/codex-cli/state.d.ts.map +1 -1
- package/dist/lib/codex-cli/state.js +18 -0
- package/dist/lib/codex-cli/writer.d.ts.map +1 -1
- package/dist/lib/codex-cli/writer.js +60 -8
- 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/account-pool-write.d.ts +3 -1
- package/dist/lib/codex-manager/account-pool-write.d.ts.map +1 -1
- package/dist/lib/codex-manager/account-pool-write.js +4 -0
- package/dist/lib/codex-manager/api-login-menu.d.ts +14 -0
- package/dist/lib/codex-manager/api-login-menu.d.ts.map +1 -0
- package/dist/lib/codex-manager/api-login-menu.js +224 -0
- package/dist/lib/codex-manager/commands/account.d.ts.map +1 -1
- package/dist/lib/codex-manager/commands/account.js +27 -0
- package/dist/lib/codex-manager/commands/best.d.ts.map +1 -1
- package/dist/lib/codex-manager/commands/best.js +6 -2
- package/dist/lib/codex-manager/commands/check.d.ts +7 -1
- package/dist/lib/codex-manager/commands/check.d.ts.map +1 -1
- package/dist/lib/codex-manager/commands/check.js +24 -3
- package/dist/lib/codex-manager/commands/forecast.d.ts.map +1 -1
- package/dist/lib/codex-manager/commands/forecast.js +5 -1
- package/dist/lib/codex-manager/commands/report.d.ts.map +1 -1
- package/dist/lib/codex-manager/commands/report.js +5 -1
- package/dist/lib/codex-manager/commands/resets.d.ts +2 -0
- package/dist/lib/codex-manager/commands/resets.d.ts.map +1 -0
- package/dist/lib/codex-manager/commands/resets.js +67 -0
- package/dist/lib/codex-manager/commands/rotation.d.ts +2 -2
- package/dist/lib/codex-manager/commands/rotation.d.ts.map +1 -1
- package/dist/lib/codex-manager/commands/rotation.js +63 -12
- package/dist/lib/codex-manager/commands/status.d.ts +12 -1
- package/dist/lib/codex-manager/commands/status.d.ts.map +1 -1
- package/dist/lib/codex-manager/commands/status.js +166 -22
- package/dist/lib/codex-manager/commands/uninstall.js +1 -1
- package/dist/lib/codex-manager/formatters/quota-formatters.d.ts.map +1 -1
- package/dist/lib/codex-manager/formatters/quota-formatters.js +3 -2
- package/dist/lib/codex-manager/health-check.d.ts +3 -0
- package/dist/lib/codex-manager/health-check.d.ts.map +1 -1
- package/dist/lib/codex-manager/health-check.js +56 -6
- package/dist/lib/codex-manager/help.d.ts.map +1 -1
- package/dist/lib/codex-manager/help.js +4 -2
- package/dist/lib/codex-manager/login-flow.d.ts.map +1 -1
- package/dist/lib/codex-manager/login-flow.js +69 -7
- package/dist/lib/codex-manager/login-menu-data.d.ts.map +1 -1
- package/dist/lib/codex-manager/login-menu-data.js +14 -5
- package/dist/lib/codex-manager/login-oauth.d.ts +3 -0
- package/dist/lib/codex-manager/login-oauth.d.ts.map +1 -1
- package/dist/lib/codex-manager/login-oauth.js +6 -1
- package/dist/lib/codex-manager/login-workspace-choice.d.ts +11 -0
- package/dist/lib/codex-manager/login-workspace-choice.d.ts.map +1 -0
- package/dist/lib/codex-manager/login-workspace-choice.js +37 -0
- package/dist/lib/codex-manager/persist-selected-account.d.ts.map +1 -1
- package/dist/lib/codex-manager/persist-selected-account.js +2 -1
- package/dist/lib/codex-manager/repair-commands.d.ts +13 -0
- package/dist/lib/codex-manager/repair-commands.d.ts.map +1 -1
- package/dist/lib/codex-manager/repair-commands.js +143 -8
- package/dist/lib/codex-manager.d.ts.map +1 -1
- package/dist/lib/codex-manager.js +25 -6
- package/dist/lib/concurrency.d.ts +3 -0
- package/dist/lib/concurrency.d.ts.map +1 -0
- package/dist/lib/concurrency.js +31 -0
- package/dist/lib/model-route-policy.d.ts +52 -0
- package/dist/lib/model-route-policy.d.ts.map +1 -0
- package/dist/lib/model-route-policy.js +159 -0
- package/dist/lib/policy/runtime-policy.d.ts +1 -0
- package/dist/lib/policy/runtime-policy.d.ts.map +1 -1
- package/dist/lib/policy/runtime-policy.js +3 -0
- package/dist/lib/preemptive-quota-scheduler.d.ts +2 -0
- package/dist/lib/preemptive-quota-scheduler.d.ts.map +1 -1
- package/dist/lib/preemptive-quota-scheduler.js +4 -0
- package/dist/lib/quota-probe.d.ts +5 -0
- package/dist/lib/quota-probe.d.ts.map +1 -1
- package/dist/lib/quota-probe.js +26 -2
- package/dist/lib/request/client-cancellation.d.ts +5 -0
- package/dist/lib/request/client-cancellation.d.ts.map +1 -0
- package/dist/lib/request/client-cancellation.js +7 -0
- package/dist/lib/request/response-outcome.d.ts +25 -0
- package/dist/lib/request/response-outcome.d.ts.map +1 -0
- package/dist/lib/request/response-outcome.js +49 -0
- package/dist/lib/request/stream-failover-runtime.d.ts +2 -1
- package/dist/lib/request/stream-failover-runtime.d.ts.map +1 -1
- package/dist/lib/request/stream-failover-runtime.js +29 -10
- package/dist/lib/runtime/account-check.d.ts.map +1 -1
- package/dist/lib/runtime/account-check.js +2 -8
- package/dist/lib/runtime/account-model-catalog.d.ts +64 -0
- package/dist/lib/runtime/account-model-catalog.d.ts.map +1 -0
- package/dist/lib/runtime/account-model-catalog.js +154 -0
- package/dist/lib/runtime/account-pool.d.ts +3 -1
- package/dist/lib/runtime/account-pool.d.ts.map +1 -1
- package/dist/lib/runtime/account-pool.js +4 -0
- package/dist/lib/runtime/account-reset-credits.d.ts +42 -0
- package/dist/lib/runtime/account-reset-credits.d.ts.map +1 -0
- package/dist/lib/runtime/account-reset-credits.js +82 -0
- package/dist/lib/runtime/api-model-capabilities.d.ts +30 -0
- package/dist/lib/runtime/api-model-capabilities.d.ts.map +1 -0
- package/dist/lib/runtime/api-model-capabilities.js +434 -0
- package/dist/lib/runtime/api-model-runtime.d.ts +42 -0
- package/dist/lib/runtime/api-model-runtime.d.ts.map +1 -0
- package/dist/lib/runtime/api-model-runtime.js +358 -0
- package/dist/lib/runtime/app-bind.d.ts +19 -1
- package/dist/lib/runtime/app-bind.d.ts.map +1 -1
- package/dist/lib/runtime/app-bind.js +79 -9
- package/dist/lib/runtime/automatic-account-checks.d.ts +31 -0
- package/dist/lib/runtime/automatic-account-checks.d.ts.map +1 -0
- package/dist/lib/runtime/automatic-account-checks.js +113 -0
- package/dist/lib/runtime/automatic-subscription-checks.d.ts +7 -0
- package/dist/lib/runtime/automatic-subscription-checks.d.ts.map +1 -0
- package/dist/lib/runtime/automatic-subscription-checks.js +57 -0
- package/dist/lib/runtime/catalog-capabilities.d.ts +8 -0
- package/dist/lib/runtime/catalog-capabilities.d.ts.map +1 -0
- package/dist/lib/runtime/catalog-capabilities.js +61 -0
- package/dist/lib/runtime/inference-activity.d.ts +15 -0
- package/dist/lib/runtime/inference-activity.d.ts.map +1 -0
- package/dist/lib/runtime/inference-activity.js +95 -0
- package/dist/lib/runtime/model-discovery-status.d.ts +100 -0
- package/dist/lib/runtime/model-discovery-status.d.ts.map +1 -0
- package/dist/lib/runtime/model-discovery-status.js +387 -0
- package/dist/lib/runtime/native-account-storage.d.ts +11 -0
- package/dist/lib/runtime/native-account-storage.d.ts.map +1 -0
- package/dist/lib/runtime/native-account-storage.js +50 -0
- package/dist/lib/runtime/native-account-sync.d.ts +19 -0
- package/dist/lib/runtime/native-account-sync.d.ts.map +1 -0
- package/dist/lib/runtime/native-account-sync.js +62 -0
- package/dist/lib/runtime/native-binding-lock.d.ts +3 -0
- package/dist/lib/runtime/native-binding-lock.d.ts.map +1 -0
- package/dist/lib/runtime/native-binding-lock.js +5 -0
- package/dist/lib/runtime/native-client-auth.d.ts +3 -0
- package/dist/lib/runtime/native-client-auth.d.ts.map +1 -0
- package/dist/lib/runtime/native-client-auth.js +42 -0
- package/dist/lib/runtime/native-provider-config.d.ts +4 -0
- package/dist/lib/runtime/native-provider-config.d.ts.map +1 -0
- package/dist/lib/runtime/native-provider-config.js +52 -0
- package/dist/lib/runtime/native-rate-limits.d.ts +18 -0
- package/dist/lib/runtime/native-rate-limits.d.ts.map +1 -0
- package/dist/lib/runtime/native-rate-limits.js +135 -0
- package/dist/lib/runtime/reset-credit-routing.d.ts +41 -0
- package/dist/lib/runtime/reset-credit-routing.d.ts.map +1 -0
- package/dist/lib/runtime/reset-credit-routing.js +66 -0
- package/dist/lib/runtime/reset-credits.d.ts +101 -0
- package/dist/lib/runtime/reset-credits.d.ts.map +1 -0
- package/dist/lib/runtime/reset-credits.js +162 -0
- package/dist/lib/runtime/response-output-history.d.ts +17 -0
- package/dist/lib/runtime/response-output-history.d.ts.map +1 -0
- package/dist/lib/runtime/response-output-history.js +85 -0
- package/dist/lib/runtime/responses-websocket.d.ts +31 -0
- package/dist/lib/runtime/responses-websocket.d.ts.map +1 -0
- package/dist/lib/runtime/responses-websocket.js +636 -0
- package/dist/lib/runtime/resume-picker.d.ts +41 -0
- package/dist/lib/runtime/resume-picker.d.ts.map +1 -0
- package/dist/lib/runtime/resume-picker.js +169 -0
- package/dist/lib/runtime/rotation-account-selection.d.ts +6 -0
- package/dist/lib/runtime/rotation-account-selection.d.ts.map +1 -1
- package/dist/lib/runtime/rotation-account-selection.js +63 -0
- package/dist/lib/runtime/rotation-proxy-state.d.ts +33 -0
- package/dist/lib/runtime/rotation-proxy-state.d.ts.map +1 -1
- package/dist/lib/runtime/rotation-server-types.d.ts +18 -0
- package/dist/lib/runtime/rotation-server-types.d.ts.map +1 -1
- package/dist/lib/runtime/runtime-capability-failures.d.ts +14 -0
- package/dist/lib/runtime/runtime-capability-failures.d.ts.map +1 -0
- package/dist/lib/runtime/runtime-capability-failures.js +76 -0
- package/dist/lib/runtime/runtime-observability.d.ts +7 -0
- package/dist/lib/runtime/runtime-observability.d.ts.map +1 -1
- package/dist/lib/runtime/runtime-observability.js +42 -2
- package/dist/lib/runtime/subscription-first-use.d.ts +10 -0
- package/dist/lib/runtime/subscription-first-use.d.ts.map +1 -0
- package/dist/lib/runtime/subscription-first-use.js +64 -0
- package/dist/lib/runtime/subscription-quota-event.d.ts +6 -0
- package/dist/lib/runtime/subscription-quota-event.d.ts.map +1 -0
- package/dist/lib/runtime/subscription-quota-event.js +31 -0
- package/dist/lib/runtime/subscription-quota-order.d.ts +17 -0
- package/dist/lib/runtime/subscription-quota-order.d.ts.map +1 -0
- package/dist/lib/runtime/subscription-quota-order.js +31 -0
- package/dist/lib/runtime/workspace-model-scopes.d.ts +15 -0
- package/dist/lib/runtime/workspace-model-scopes.d.ts.map +1 -0
- package/dist/lib/runtime/workspace-model-scopes.js +34 -0
- package/dist/lib/runtime-constants.d.ts +3 -0
- package/dist/lib/runtime-constants.d.ts.map +1 -1
- package/dist/lib/runtime-constants.js +5 -0
- package/dist/lib/runtime-paths.d.ts.map +1 -1
- package/dist/lib/runtime-paths.js +4 -1
- package/dist/lib/runtime-rotation-proxy.d.ts.map +1 -1
- package/dist/lib/runtime-rotation-proxy.js +852 -42
- package/dist/lib/schemas.d.ts +20 -0
- package/dist/lib/schemas.d.ts.map +1 -1
- package/dist/lib/schemas.js +3 -0
- package/dist/lib/storage/credential-sidecars.d.ts +13 -0
- package/dist/lib/storage/credential-sidecars.d.ts.map +1 -0
- package/dist/lib/storage/credential-sidecars.js +52 -0
- package/dist/lib/storage/file-lock.d.ts +9 -0
- package/dist/lib/storage/file-lock.d.ts.map +1 -0
- package/dist/lib/storage/file-lock.js +186 -0
- package/dist/lib/storage/flagged-storage.d.ts.map +1 -1
- package/dist/lib/storage/flagged-storage.js +12 -0
- package/dist/lib/storage/path-state.d.ts +2 -0
- package/dist/lib/storage/path-state.d.ts.map +1 -1
- package/dist/lib/storage/path-state.js +9 -0
- package/dist/lib/storage/paths.js +4 -4
- package/dist/lib/storage/pending-auth.d.ts +17 -0
- package/dist/lib/storage/pending-auth.d.ts.map +1 -0
- package/dist/lib/storage/pending-auth.js +153 -0
- package/dist/lib/storage/public-types.d.ts +13 -0
- package/dist/lib/storage/public-types.d.ts.map +1 -1
- package/dist/lib/storage/snapshot-merge.d.ts +16 -0
- package/dist/lib/storage/snapshot-merge.d.ts.map +1 -0
- package/dist/lib/storage/snapshot-merge.js +206 -0
- package/dist/lib/storage/transactions.d.ts.map +1 -1
- package/dist/lib/storage/transactions.js +5 -4
- package/dist/lib/storage.d.ts +6 -0
- package/dist/lib/storage.d.ts.map +1 -1
- package/dist/lib/storage.js +77 -7
- package/dist/lib/ui/auth-menu-builder.d.ts +2 -0
- package/dist/lib/ui/auth-menu-builder.d.ts.map +1 -1
- package/dist/lib/ui/auth-menu-builder.js +1 -0
- package/dist/lib/ui/auth-menu.d.ts.map +1 -1
- package/dist/lib/ui/auth-menu.js +1 -0
- package/dist/lib/ui/check-progress.d.ts +8 -0
- package/dist/lib/ui/check-progress.d.ts.map +1 -0
- package/dist/lib/ui/check-progress.js +30 -0
- package/dist/lib/ui/format.d.ts +3 -1
- package/dist/lib/ui/format.d.ts.map +1 -1
- package/dist/lib/ui/format.js +8 -0
- package/dist/lib/ui/ui-copy.d.ts +2 -2
- package/dist/lib/ui/ui-copy.js +2 -2
- package/dist/lib/usage/pricing.js +2 -2
- package/dist/lib/usage/usage-extraction.d.ts +3 -1
- package/dist/lib/usage/usage-extraction.d.ts.map +1 -1
- package/dist/lib/usage/usage-extraction.js +46 -9
- package/package.json +7 -4
- package/scripts/codex-app-router.js +7 -0
- package/scripts/codex-bin-resolver.js +40 -8
- package/scripts/codex-routing.js +1 -0
- package/scripts/codex.js +100 -22
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { AccountMetadataV3, AccountStorageV3 } from "./public-types.js";
|
|
2
|
+
type CooldownRow = Pick<AccountMetadataV3, "coolingDownUntil" | "cooldownReason" | "refreshToken">;
|
|
3
|
+
/**
|
|
4
|
+
* Preserve concurrent cooldown observations while allowing an unchanged blocker to clear.
|
|
5
|
+
* An auth-failure cooldown belongs to the credential it was recorded against: once
|
|
6
|
+
* the merge keeps a different refresh token (e.g. we won a single-use refresh the
|
|
7
|
+
* other process lost with invalid_grant), that side's auth blocker is obsolete.
|
|
8
|
+
* Other cooldown reasons are observations about the account and are kept.
|
|
9
|
+
*/
|
|
10
|
+
export declare function mergeAccountCooldown(base: CooldownRow, disk: CooldownRow, local: CooldownRow): Pick<AccountMetadataV3, "coolingDownUntil" | "cooldownReason">;
|
|
11
|
+
/** Three-way persistence under the file transaction lock. Disk deletions win over stale runtime state. */
|
|
12
|
+
export declare function mergeAccountSnapshot(base: AccountStorageV3 | null, current: AccountStorageV3 | null, local: AccountStorageV3): AccountStorageV3;
|
|
13
|
+
/** Save observations independently of unresolved user-edit conflicts. Inventory stays disk-owned. */
|
|
14
|
+
export declare function mergeAccountRuntimeObservations(base: AccountStorageV3, current: AccountStorageV3, local: AccountStorageV3): AccountStorageV3;
|
|
15
|
+
export {};
|
|
16
|
+
//# sourceMappingURL=snapshot-merge.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"snapshot-merge.d.ts","sourceRoot":"","sources":["../../../lib/storage/snapshot-merge.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AA8D7E,KAAK,WAAW,GAAG,IAAI,CAAC,iBAAiB,EAAE,kBAAkB,GAAG,gBAAgB,GAAG,cAAc,CAAC,CAAC;AACnG;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,WAAW,EAAE,KAAK,EAAE,WAAW,GAAG,IAAI,CAAC,iBAAiB,EAAE,kBAAkB,GAAG,gBAAgB,CAAC,CAW7J;AAqBD,0GAA0G;AAC1G,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,gBAAgB,GAAG,IAAI,EAAE,OAAO,EAAE,gBAAgB,GAAG,IAAI,EAAE,KAAK,EAAE,gBAAgB,GAAG,gBAAgB,CA0E/I;AAED,qGAAqG;AACrG,wBAAgB,+BAA+B,CAAC,IAAI,EAAE,gBAAgB,EAAE,OAAO,EAAE,gBAAgB,EAAE,KAAK,EAAE,gBAAgB,GAAG,gBAAgB,CAmB5I"}
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
import { getAccountIdentityKey } from "./identity.js";
|
|
2
|
+
import { isRecord } from "../utils.js";
|
|
3
|
+
const equal = (a, b) => JSON.stringify(a) === JSON.stringify(b);
|
|
4
|
+
function conflict() {
|
|
5
|
+
throw Object.assign(Error("Account storage changed concurrently; reload before saving."), { code: "ESTALE" });
|
|
6
|
+
}
|
|
7
|
+
function mergeValue(base, disk, local) {
|
|
8
|
+
if (equal(base, local) || equal(disk, local))
|
|
9
|
+
return structuredClone(disk);
|
|
10
|
+
if (equal(base, disk))
|
|
11
|
+
return structuredClone(local);
|
|
12
|
+
if (base === undefined && isRecord(disk) && isRecord(local))
|
|
13
|
+
base = {};
|
|
14
|
+
if (isRecord(base) && isRecord(disk) && isRecord(local)) {
|
|
15
|
+
const result = Object.create(null);
|
|
16
|
+
for (const key of new Set([...Object.keys(base), ...Object.keys(disk), ...Object.keys(local)])) {
|
|
17
|
+
const value = mergeValue(base[key], disk[key], local[key]);
|
|
18
|
+
if (value !== undefined)
|
|
19
|
+
result[key] = value;
|
|
20
|
+
}
|
|
21
|
+
return result;
|
|
22
|
+
}
|
|
23
|
+
return conflict();
|
|
24
|
+
}
|
|
25
|
+
const byId = (rows) => new Map((rows ?? []).map(row => [row.id, row]));
|
|
26
|
+
/**
|
|
27
|
+
* Merge workspaces by id, never by array position. Fields changed on one side
|
|
28
|
+
* win; enabled/disabledAt move as one unit, and when both sides changed it an
|
|
29
|
+
* intentional re-enable wins over a disable. Other two-sided edits conflict.
|
|
30
|
+
* Removal follows the account rule: a record removed on either side stays
|
|
31
|
+
* removed.
|
|
32
|
+
*/
|
|
33
|
+
function mergeWorkspaces(base, disk, local) {
|
|
34
|
+
const before = byId(base.workspaces), onDisk = byId(disk.workspaces), next = byId(local.workspaces);
|
|
35
|
+
const ids = [...onDisk.keys(), ...[...next.keys()].filter(id => !onDisk.has(id) && !before.has(id))];
|
|
36
|
+
const workspaces = [];
|
|
37
|
+
for (const id of ids) {
|
|
38
|
+
const b = before.get(id), d = onDisk.get(id), l = next.get(id);
|
|
39
|
+
if (!d || !l) {
|
|
40
|
+
// Present on one side only: an addition survives, a removal wins.
|
|
41
|
+
const added = d ?? l;
|
|
42
|
+
if (!b && added)
|
|
43
|
+
workspaces.push(structuredClone(added));
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
const state = (row) => ({ enabled: row?.enabled, disabledAt: row?.disabledAt });
|
|
47
|
+
const stateSource = equal(state(b), state(l)) ? d : equal(state(b), state(d)) ? l : d.enabled !== false ? d : l.enabled !== false ? l : (d.disabledAt ?? 0) >= (l.disabledAt ?? 0) ? d : l;
|
|
48
|
+
const pick = (row) => ({ ...row, enabled: stateSource.enabled, disabledAt: stateSource.disabledAt });
|
|
49
|
+
const merged = mergeValue(b ? pick(b) : undefined, pick(d), pick(l));
|
|
50
|
+
if (merged.disabledAt === undefined)
|
|
51
|
+
delete merged.disabledAt;
|
|
52
|
+
workspaces.push(merged);
|
|
53
|
+
}
|
|
54
|
+
const currentId = (row) => row.workspaces?.[row.currentWorkspaceIndex ?? 0]?.id;
|
|
55
|
+
const chosen = currentId(local) !== currentId(base) ? currentId(local) : currentId(disk);
|
|
56
|
+
let index = workspaces.findIndex(row => row.id === chosen);
|
|
57
|
+
if (index < 0 || workspaces[index]?.enabled === false) {
|
|
58
|
+
const enabled = workspaces.findIndex(row => row.enabled !== false);
|
|
59
|
+
index = enabled >= 0 ? enabled : Math.max(index, 0);
|
|
60
|
+
}
|
|
61
|
+
const untracked = [base, disk, local].every(row => row.currentWorkspaceIndex === undefined);
|
|
62
|
+
return { workspaces, currentWorkspaceIndex: untracked && index === 0 ? undefined : index };
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Preserve concurrent cooldown observations while allowing an unchanged blocker to clear.
|
|
66
|
+
* An auth-failure cooldown belongs to the credential it was recorded against: once
|
|
67
|
+
* the merge keeps a different refresh token (e.g. we won a single-use refresh the
|
|
68
|
+
* other process lost with invalid_grant), that side's auth blocker is obsolete.
|
|
69
|
+
* Other cooldown reasons are observations about the account and are kept.
|
|
70
|
+
*/
|
|
71
|
+
export function mergeAccountCooldown(base, disk, local) {
|
|
72
|
+
const keptToken = local.refreshToken === base.refreshToken ? disk.refreshToken : local.refreshToken;
|
|
73
|
+
const current = (row) => row.cooldownReason === "auth-failure" && row.refreshToken !== keptToken
|
|
74
|
+
? { ...row, coolingDownUntil: undefined, cooldownReason: undefined } : row;
|
|
75
|
+
disk = current(disk);
|
|
76
|
+
local = current(local);
|
|
77
|
+
const cleared = base.coolingDownUntil !== undefined && ((disk.coolingDownUntil === undefined && (local.coolingDownUntil === undefined || local.coolingDownUntil === base.coolingDownUntil)) ||
|
|
78
|
+
(local.coolingDownUntil === undefined && disk.coolingDownUntil === base.coolingDownUntil));
|
|
79
|
+
const cooldown = (disk.coolingDownUntil ?? 0) >= (local.coolingDownUntil ?? 0) ? disk : local;
|
|
80
|
+
return { coolingDownUntil: cleared ? undefined : cooldown.coolingDownUntil, cooldownReason: cleared ? undefined : cooldown.cooldownReason };
|
|
81
|
+
}
|
|
82
|
+
/** Runtime observations commute; user-owned edits retain conflict detection. */
|
|
83
|
+
function mergeRuntimeAccount(base, disk, local) {
|
|
84
|
+
const limits = {};
|
|
85
|
+
for (const key of new Set([...Object.keys(disk.rateLimitResetTimes ?? {}), ...Object.keys(local.rateLimitResetTimes ?? {})])) {
|
|
86
|
+
const before = base.rateLimitResetTimes?.[key], a = disk.rateLimitResetTimes?.[key], b = local.rateLimitResetTimes?.[key];
|
|
87
|
+
// An explicit clear is an operation, not a new observation.
|
|
88
|
+
const clear = before !== undefined && ((a === undefined && b === before) || (b === undefined && a === before) || (a === undefined && b === undefined));
|
|
89
|
+
const value = clear ? undefined : Math.max(a ?? 0, b ?? 0);
|
|
90
|
+
if (value !== undefined)
|
|
91
|
+
limits[key] = value;
|
|
92
|
+
}
|
|
93
|
+
const runtime = {
|
|
94
|
+
rateLimitResetTimes: Object.keys(limits).length ? limits : undefined,
|
|
95
|
+
...mergeAccountCooldown(base, disk, local),
|
|
96
|
+
lastSwitchReason: local.lastSwitchReason ?? disk.lastSwitchReason,
|
|
97
|
+
...(disk.workspaces || local.workspaces ? mergeWorkspaces(base, disk, local) : {}),
|
|
98
|
+
};
|
|
99
|
+
// Omitted and true both mean enabled; serialization does not express a user edit.
|
|
100
|
+
const normalized = (row) => ({ ...row, enabled: row.enabled === false ? false : undefined });
|
|
101
|
+
return mergeValue(normalized(base), { ...normalized(disk), ...runtime }, { ...normalized(local), ...runtime });
|
|
102
|
+
}
|
|
103
|
+
/** Three-way persistence under the file transaction lock. Disk deletions win over stale runtime state. */
|
|
104
|
+
export function mergeAccountSnapshot(base, current, local) {
|
|
105
|
+
if (!base) {
|
|
106
|
+
if (!current)
|
|
107
|
+
return structuredClone(local);
|
|
108
|
+
// An unavailable initial read is not authority to replace the inventory.
|
|
109
|
+
base = { version: 3, accounts: [], activeIndex: 0 };
|
|
110
|
+
}
|
|
111
|
+
if (!current && base.accounts.length === 0 && "restoreReason" in base && base.restoreReason === "missing-storage")
|
|
112
|
+
current = { version: 3, accounts: [], activeIndex: 0 };
|
|
113
|
+
if (!current)
|
|
114
|
+
return conflict();
|
|
115
|
+
const aliases = new Map();
|
|
116
|
+
for (const row of [...base.accounts, ...current.accounts, ...local.accounts]) {
|
|
117
|
+
const key = getAccountIdentityKey(row);
|
|
118
|
+
if (!key || !row.recordId)
|
|
119
|
+
continue;
|
|
120
|
+
const ids = aliases.get(key) ?? new Set();
|
|
121
|
+
ids.add(row.recordId);
|
|
122
|
+
aliases.set(key, ids);
|
|
123
|
+
}
|
|
124
|
+
const identity = (row) => {
|
|
125
|
+
if (row.recordId)
|
|
126
|
+
return row.recordId;
|
|
127
|
+
const key = getAccountIdentityKey(row), ids = key ? aliases.get(key) : undefined;
|
|
128
|
+
return ids?.size === 1 ? [...ids][0] : key;
|
|
129
|
+
};
|
|
130
|
+
const index = (rows) => {
|
|
131
|
+
const map = new Map();
|
|
132
|
+
for (const row of rows) {
|
|
133
|
+
const key = identity(row);
|
|
134
|
+
if (!key || map.has(key))
|
|
135
|
+
return conflict();
|
|
136
|
+
map.set(key, row);
|
|
137
|
+
}
|
|
138
|
+
return map;
|
|
139
|
+
};
|
|
140
|
+
const old = index(base.accounts), disk = index(current.accounts), proposed = index(local.accounts);
|
|
141
|
+
const accounts = [];
|
|
142
|
+
for (const [key, row] of disk) {
|
|
143
|
+
const prior = old.get(key), next = proposed.get(key);
|
|
144
|
+
if (prior && !next)
|
|
145
|
+
continue; // Explicit local removal of a previously known record.
|
|
146
|
+
if (!prior) {
|
|
147
|
+
if (next && !equal(row, next))
|
|
148
|
+
conflict();
|
|
149
|
+
accounts.push(structuredClone(row));
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
152
|
+
if (!next)
|
|
153
|
+
continue;
|
|
154
|
+
const lastUsed = Math.max(row.lastUsed, next.lastUsed);
|
|
155
|
+
const merged = mergeRuntimeAccount(prior, { ...row, lastUsed }, { ...next, lastUsed });
|
|
156
|
+
accounts.push(merged);
|
|
157
|
+
}
|
|
158
|
+
for (const [key, row] of proposed)
|
|
159
|
+
if (!old.has(key) && !disk.has(key))
|
|
160
|
+
accounts.push(structuredClone(row));
|
|
161
|
+
const pointer = (storage, value) => { const row = value === undefined ? undefined : storage.accounts[value]; return row ? identity(row) : undefined; };
|
|
162
|
+
const position = (id) => id === undefined ? undefined : accounts.findIndex(row => identity(row) === id);
|
|
163
|
+
const pick = (before, onDisk, next) => {
|
|
164
|
+
const oldId = pointer(base, before), diskId = pointer(current, onDisk), localId = pointer(local, next);
|
|
165
|
+
return position(localId !== oldId ? localId : diskId);
|
|
166
|
+
};
|
|
167
|
+
// Pointer metadata must be compared by identity, never by mutable list positions.
|
|
168
|
+
const fields = (storage) => Object.fromEntries(Object.entries(storage).filter(([key]) => !["accounts", "activeIndex", "activeIndexByFamily", "pinnedAccountIndex", "restoreEligible", "restoreReason"].includes(key)));
|
|
169
|
+
const result = { ...mergeValue(fields(base), fields(current), fields(local)), accounts, activeIndex: Math.max(0, pick(base.activeIndex, current.activeIndex, local.activeIndex) ?? 0) };
|
|
170
|
+
result.activeIndexByFamily = {};
|
|
171
|
+
for (const family of new Set([...Object.keys(base.activeIndexByFamily ?? {}), ...Object.keys(current.activeIndexByFamily ?? {}), ...Object.keys(local.activeIndexByFamily ?? {})])) {
|
|
172
|
+
const f = family;
|
|
173
|
+
result.activeIndexByFamily[f] = Math.max(0, pick(base.activeIndexByFamily?.[f], current.activeIndexByFamily?.[f], local.activeIndexByFamily?.[f]) ?? 0);
|
|
174
|
+
}
|
|
175
|
+
const pinned = pick(base.pinnedAccountIndex, current.pinnedAccountIndex, local.pinnedAccountIndex);
|
|
176
|
+
if (pinned !== undefined && pinned >= 0)
|
|
177
|
+
result.pinnedAccountIndex = pinned;
|
|
178
|
+
return result;
|
|
179
|
+
}
|
|
180
|
+
/** Save observations independently of unresolved user-edit conflicts. Inventory stays disk-owned. */
|
|
181
|
+
export function mergeAccountRuntimeObservations(base, current, local) {
|
|
182
|
+
const observations = structuredClone(base);
|
|
183
|
+
const matches = (a, b) => a.recordId && b.recordId
|
|
184
|
+
? a.recordId === b.recordId : getAccountIdentityKey(a) === getAccountIdentityKey(b);
|
|
185
|
+
for (const prior of observations.accounts) {
|
|
186
|
+
const next = local.accounts.find(row => matches(prior, row));
|
|
187
|
+
if (!next)
|
|
188
|
+
continue;
|
|
189
|
+
prior.lastUsed = Math.max(prior.lastUsed, next.lastUsed);
|
|
190
|
+
for (const field of ["rateLimitResetTimes", "coolingDownUntil", "cooldownReason", "lastSwitchReason"]) {
|
|
191
|
+
if (next[field] === undefined)
|
|
192
|
+
delete prior[field];
|
|
193
|
+
else
|
|
194
|
+
Object.assign(prior, { [field]: structuredClone(next[field]) });
|
|
195
|
+
}
|
|
196
|
+
// Preserve runtime workspace disables without committing labels or workspace selection.
|
|
197
|
+
for (const workspace of prior.workspaces ?? []) {
|
|
198
|
+
const updated = next.workspaces?.find(row => row.id === workspace.id);
|
|
199
|
+
if (updated) {
|
|
200
|
+
workspace.enabled = updated.enabled;
|
|
201
|
+
workspace.disabledAt = updated.disabledAt;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
return mergeAccountSnapshot(base, current, observations);
|
|
206
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transactions.d.ts","sourceRoot":"","sources":["../../../lib/storage/transactions.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,uBAAuB,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"transactions.d.ts","sourceRoot":"","sources":["../../../lib/storage/transactions.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,uBAAuB,EAAE,MAAM,mBAAmB,CAAC;AAGnF,MAAM,MAAM,wBAAwB,GAAG;IACtC,QAAQ,EAAE,gBAAgB,GAAG,IAAI,CAAC;IAClC,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,OAAO,CAAC;CAChB,CAAC;AAOF;;;;;;GAMG;AACH,wBAAgB,iBAAiB,IAAI,OAAO,CAE3C;AAED,wBAAgB,2BAA2B,IACxC,wBAAwB,GACxB,SAAS,CAEX;AAED,wBAAgB,+BAA+B,CAAC,CAAC,EAChD,KAAK,EAAE,wBAAwB,EAC/B,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAClB,OAAO,CAAC,CAAC,CAAC,CAEZ;AAED,wBAAgB,eAAe,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CASnE;AAED,wBAAsB,6BAA6B,CAAC,CAAC,EACpD,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,EACf,IAAI,EAAE;IACL,cAAc,EAAE,MAAM,MAAM,CAAC;IAC7B,WAAW,EAAE,MAAM,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAAC;IACpD,YAAY,EAAE,CAAC,OAAO,EAAE,gBAAgB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAC3D,GACC,OAAO,CAAC,CAAC,CAAC,CAgBZ;AAED,wBAAsB,uCAAuC,CAAC,CAAC,EAC9D,OAAO,EAAE,CACR,OAAO,EAAE,gBAAgB,GAAG,IAAI,EAChC,OAAO,EAAE,CACR,cAAc,EAAE,gBAAgB,EAChC,cAAc,EAAE,uBAAuB,KACnC,OAAO,CAAC,IAAI,CAAC,EAClB,cAAc,EAAE,uBAAuB,KACnC,OAAO,CAAC,CAAC,CAAC,EACf,IAAI,EAAE;IACL,cAAc,EAAE,MAAM,MAAM,CAAC;IAC7B,WAAW,EAAE,MAAM,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAAC;IACpD,kBAAkB,CAAC,EAAE,MAAM,OAAO,CAAC,uBAAuB,CAAC,CAAC;IAC5D,YAAY,EAAE,CAAC,OAAO,EAAE,gBAAgB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3D,mBAAmB,EAAE,CAAC,OAAO,EAAE,uBAAuB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACzE,iCAAiC,EAAE,CAClC,OAAO,EAAE,gBAAgB,GAAG,IAAI,GAAG,SAAS,KACxC,gBAAgB,CAAC;IACtB,gBAAgB,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,aAAa,EAAE,OAAO,KAAK,IAAI,CAAC;CACnE,GACC,OAAO,CAAC,CAAC,CAAC,CA6CZ"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
2
|
+
import { withFileTransactionLock } from "./file-lock.js";
|
|
2
3
|
let storageMutex = Promise.resolve();
|
|
3
4
|
const transactionSnapshotContext = new AsyncLocalStorage();
|
|
4
5
|
const storageLockHeldContext = new AsyncLocalStorage();
|
|
@@ -29,7 +30,7 @@ export function withStorageLock(fn) {
|
|
|
29
30
|
.finally(() => releaseLock());
|
|
30
31
|
}
|
|
31
32
|
export async function withAccountStorageTransaction(handler, deps) {
|
|
32
|
-
return withStorageLock(async () => {
|
|
33
|
+
return withStorageLock(() => withFileTransactionLock(deps.getStoragePath(), async () => {
|
|
33
34
|
const state = {
|
|
34
35
|
snapshot: await deps.loadCurrent(),
|
|
35
36
|
storagePath: deps.getStoragePath(),
|
|
@@ -41,10 +42,10 @@ export async function withAccountStorageTransaction(handler, deps) {
|
|
|
41
42
|
state.snapshot = storage;
|
|
42
43
|
};
|
|
43
44
|
return transactionSnapshotContext.run(state, () => handler(current, persist));
|
|
44
|
-
});
|
|
45
|
+
}));
|
|
45
46
|
}
|
|
46
47
|
export async function withAccountAndFlaggedStorageTransaction(handler, deps) {
|
|
47
|
-
return withStorageLock(async () => {
|
|
48
|
+
return withStorageLock(() => withFileTransactionLock(deps.getStoragePath(), async () => {
|
|
48
49
|
const state = {
|
|
49
50
|
snapshot: await deps.loadCurrent(),
|
|
50
51
|
storagePath: deps.getStoragePath(),
|
|
@@ -78,5 +79,5 @@ export async function withAccountAndFlaggedStorageTransaction(handler, deps) {
|
|
|
78
79
|
}
|
|
79
80
|
};
|
|
80
81
|
return transactionSnapshotContext.run(state, () => handler(current, persist, currentFlagged));
|
|
81
|
-
});
|
|
82
|
+
}));
|
|
82
83
|
}
|
package/dist/lib/storage.d.ts
CHANGED
|
@@ -5,6 +5,8 @@ export { formatStorageErrorHint, toStorageError, } from "./storage/error-hints.j
|
|
|
5
5
|
export { getAccountIdentityKey, normalizeEmailKey, } from "./storage/identity.js";
|
|
6
6
|
import type { AccountMetadataV3, AccountStorageV3, CooldownReason, FlaggedAccountMetadataV1, FlaggedAccountStorageV1, RateLimitStateV3 } from "./storage/public-types.js";
|
|
7
7
|
import { type NamedBackupSummary } from "./storage/named-backups.js";
|
|
8
|
+
import { recordPendingAuth } from "./storage/pending-auth.js";
|
|
9
|
+
export { recordPendingAuth };
|
|
8
10
|
export type { StorageHealthSummary, CooldownReason, RateLimitStateV3, AccountMetadataV3, AccountStorageV3, FlaggedAccountMetadataV1, FlaggedAccountStorageV1, BackupMetadata, RestoreAssessment, NamedBackupSummary, };
|
|
9
11
|
type AccountLike = {
|
|
10
12
|
accountId?: string;
|
|
@@ -142,6 +144,8 @@ export declare function loadAccounts(): Promise<AccountStorageV3 | null>;
|
|
|
142
144
|
export declare function getBackupMetadata(): Promise<BackupMetadata>;
|
|
143
145
|
export declare function getRestoreAssessment(): Promise<RestoreAssessment>;
|
|
144
146
|
export declare function inspectStorageHealth(): Promise<StorageHealthSummary>;
|
|
147
|
+
/** Code for a merge base that cannot be read; distinct from an ESTALE edit conflict. */
|
|
148
|
+
export declare const ACCOUNT_STORAGE_UNREADABLE = "EACCOUNTSUNREADABLE";
|
|
145
149
|
export declare function withAccountStorageTransaction<T>(handler: (current: AccountStorageV3 | null, persist: (storage: AccountStorageV3) => Promise<void>) => Promise<T>): Promise<T>;
|
|
146
150
|
export declare function withAccountAndFlaggedStorageTransaction<T>(handler: (current: AccountStorageV3 | null, persist: (accountStorage: AccountStorageV3, flaggedStorage: FlaggedAccountStorageV1) => Promise<void>, currentFlagged: FlaggedAccountStorageV1) => Promise<T>): Promise<T>;
|
|
147
151
|
export declare function withFlaggedStorageTransaction<T>(handler: (current: FlaggedAccountStorageV1, persist: (storage: FlaggedAccountStorageV1) => Promise<void>) => Promise<T>): Promise<T>;
|
|
@@ -152,6 +156,8 @@ export declare function withFlaggedStorageTransaction<T>(handler: (current: Flag
|
|
|
152
156
|
* @param storage - Account storage data to save
|
|
153
157
|
* @throws StorageError with platform-aware hints on failure
|
|
154
158
|
*/
|
|
159
|
+
/** Clone a loaded working snapshot without losing its optimistic-concurrency baseline. */
|
|
160
|
+
export declare function cloneTrackedAccountStorage(storage: AccountStorageV3): AccountStorageV3;
|
|
155
161
|
export declare function saveAccounts(storage: AccountStorageV3): Promise<void>;
|
|
156
162
|
/**
|
|
157
163
|
* Deletes the account storage file from disk.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"storage.d.ts","sourceRoot":"","sources":["../../lib/storage.ts"],"names":[],"mappings":"AA+BA,OAAO,EACN,KAAK,cAAc,EAGnB,KAAK,iBAAiB,EAEtB,MAAM,8BAA8B,CAAC;AAkBtC,OAAO,EAEN,KAAK,oBAAoB,EACzB,MAAM,qBAAqB,CAAC;AAe7B,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EACN,sBAAsB,EACtB,cAAc,GACd,MAAM,0BAA0B,CAAC;AAClC,OAAO,EACN,qBAAqB,EACrB,iBAAiB,GACjB,MAAM,uBAAuB,CAAC;AAa/B,OAAO,KAAK,EACX,iBAAiB,EACjB,gBAAgB,EAChB,cAAc,EACd,wBAAwB,EACxB,uBAAuB,EACvB,gBAAgB,EAChB,MAAM,2BAA2B,CAAC;AAEnC,OAAO,EAEN,KAAK,kBAAkB,EACvB,MAAM,4BAA4B,CAAC;
|
|
1
|
+
{"version":3,"file":"storage.d.ts","sourceRoot":"","sources":["../../lib/storage.ts"],"names":[],"mappings":"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"}
|
package/dist/lib/storage.js
CHANGED
|
@@ -50,7 +50,16 @@ import { buildRestoreAssessment } from "./storage/restore-assessment.js";
|
|
|
50
50
|
import { restoreAccountsFromBackupEntry } from "./storage/restore-backup-entry.js";
|
|
51
51
|
import { createEmptyStorageWithRestoreMetadata as createEmptyStorageWithMetadata, withRestoreMetadata, } from "./storage/restore-metadata.js";
|
|
52
52
|
import { loadAccountsFromPath, parseAndNormalizeStorage, } from "./storage/storage-parser.js";
|
|
53
|
-
import { getTransactionSnapshotState, isStorageLockHeld, runInTransactionSnapshotContext, withAccountStorageTransaction as runWithAccountStorageTransaction, withStorageLock, } from "./storage/transactions.js";
|
|
53
|
+
import { getTransactionSnapshotState, isStorageLockHeld, runInTransactionSnapshotContext, withAccountStorageTransaction as runWithAccountStorageTransaction, withStorageLock as withProcessStorageLock, } from "./storage/transactions.js";
|
|
54
|
+
import { withFileTransactionLock } from "./storage/file-lock.js";
|
|
55
|
+
import { mergeAccountSnapshot } from "./storage/snapshot-merge.js";
|
|
56
|
+
import { applyPendingAuth, clearPendingAuth, prunePendingAuth, recordPendingAuth } from "./storage/pending-auth.js";
|
|
57
|
+
export { recordPendingAuth };
|
|
58
|
+
const loadedAccountSnapshots = new WeakMap();
|
|
59
|
+
function withStorageLock(action) {
|
|
60
|
+
const path = getStoragePath();
|
|
61
|
+
return withProcessStorageLock(() => withFileTransactionLock(path, action));
|
|
62
|
+
}
|
|
54
63
|
const log = createLogger("storage");
|
|
55
64
|
const ACCOUNTS_FILE_NAME = "openai-codex-accounts.json";
|
|
56
65
|
const FLAGGED_ACCOUNTS_FILE_NAME = "openai-codex-flagged-accounts.json";
|
|
@@ -1069,7 +1078,10 @@ export function readPinAndGenFromDisk(path, options) {
|
|
|
1069
1078
|
* @returns AccountStorageV3 if file exists and is valid, null otherwise
|
|
1070
1079
|
*/
|
|
1071
1080
|
export async function loadAccounts() {
|
|
1072
|
-
|
|
1081
|
+
const storage = await applyPendingAuth(getStoragePath(), await loadAccountsInternal(saveAccounts));
|
|
1082
|
+
if (storage)
|
|
1083
|
+
loadedAccountSnapshots.set(storage, structuredClone(storage));
|
|
1084
|
+
return storage;
|
|
1073
1085
|
}
|
|
1074
1086
|
export async function getBackupMetadata() {
|
|
1075
1087
|
const storagePath = getStoragePath();
|
|
@@ -1456,6 +1468,13 @@ async function loadAccountsForExport() {
|
|
|
1456
1468
|
}
|
|
1457
1469
|
}
|
|
1458
1470
|
async function saveAccountsUnlocked(storage) {
|
|
1471
|
+
await saveAccountsUnlockedToDisk(storage);
|
|
1472
|
+
// A persisted (or superseded) pending rotated credential is no longer needed.
|
|
1473
|
+
await prunePendingAuth(getStoragePath(), storage).catch((error) => {
|
|
1474
|
+
log.warn("Failed to prune pending rotated credentials", { code: typeof error.code === "string" && /^[A-Z_]{1,40}$/.test(error.code ?? "") ? error.code : "INVALID_PENDING_AUTH" });
|
|
1475
|
+
});
|
|
1476
|
+
}
|
|
1477
|
+
async function saveAccountsUnlockedToDisk(storage) {
|
|
1459
1478
|
const path = getStoragePath();
|
|
1460
1479
|
const resetMarkerPath = getIntentionalResetMarkerPath(path);
|
|
1461
1480
|
const walPath = getAccountsWalPath(path);
|
|
@@ -1577,10 +1596,40 @@ function cloneFlaggedStorageForPersistence(storage) {
|
|
|
1577
1596
|
accounts: structuredClone(storage?.accounts ?? []),
|
|
1578
1597
|
};
|
|
1579
1598
|
}
|
|
1599
|
+
/** Code for a merge base that cannot be read; distinct from an ESTALE edit conflict. */
|
|
1600
|
+
export const ACCOUNT_STORAGE_UNREADABLE = "EACCOUNTSUNREADABLE";
|
|
1601
|
+
async function loadPrimaryAccountsForMerge() {
|
|
1602
|
+
const path = getStoragePath();
|
|
1603
|
+
if (existsSync(getIntentionalResetMarkerPath(path))) {
|
|
1604
|
+
return createEmptyStorageWithMetadata(false, "intentional-reset");
|
|
1605
|
+
}
|
|
1606
|
+
try {
|
|
1607
|
+
const { normalized } = await loadAccountsFromPath(path, {
|
|
1608
|
+
normalizeAccountStorage,
|
|
1609
|
+
isRecord,
|
|
1610
|
+
});
|
|
1611
|
+
if (!normalized)
|
|
1612
|
+
throw new Error("Invalid primary account storage");
|
|
1613
|
+
// The merge base must carry pending rotated credentials too, or a merge
|
|
1614
|
+
// would keep the spent token that is still on disk.
|
|
1615
|
+
return applyPendingAuth(path, normalized);
|
|
1616
|
+
}
|
|
1617
|
+
catch (cause) {
|
|
1618
|
+
const code = cause.code;
|
|
1619
|
+
// The journal is written before the primary and removed after it, so a
|
|
1620
|
+
// journal beside a missing primary is the newest state, not a stale
|
|
1621
|
+
// backup. Without one, the deletion is authoritative.
|
|
1622
|
+
if (code === "ENOENT")
|
|
1623
|
+
return applyPendingAuth(path, await loadAccountsFromJournal(path, { silent: true }));
|
|
1624
|
+
// A locked or torn primary may hold newer state than any backup, so it is
|
|
1625
|
+
// never replaced by a recovered copy here.
|
|
1626
|
+
throw Object.assign(new Error(`Account storage could not be read${typeof code === "string" ? ` (${code})` : ""}; nothing was saved.`), { code: ACCOUNT_STORAGE_UNREADABLE, cause });
|
|
1627
|
+
}
|
|
1628
|
+
}
|
|
1580
1629
|
export async function withAccountStorageTransaction(handler) {
|
|
1581
1630
|
return runWithAccountStorageTransaction(handler, {
|
|
1582
1631
|
getStoragePath,
|
|
1583
|
-
loadCurrent:
|
|
1632
|
+
loadCurrent: loadPrimaryAccountsForMerge,
|
|
1584
1633
|
saveAccounts: saveAccountsUnlocked,
|
|
1585
1634
|
});
|
|
1586
1635
|
}
|
|
@@ -1588,7 +1637,7 @@ export async function withAccountAndFlaggedStorageTransaction(handler) {
|
|
|
1588
1637
|
return withStorageLock(async () => {
|
|
1589
1638
|
const storagePath = getStoragePath();
|
|
1590
1639
|
const state = {
|
|
1591
|
-
snapshot: await
|
|
1640
|
+
snapshot: await loadPrimaryAccountsForMerge(),
|
|
1592
1641
|
storagePath,
|
|
1593
1642
|
active: true,
|
|
1594
1643
|
};
|
|
@@ -1596,7 +1645,8 @@ export async function withAccountAndFlaggedStorageTransaction(handler) {
|
|
|
1596
1645
|
const currentFlagged = await loadFlaggedAccounts();
|
|
1597
1646
|
const persist = async (accountStorage, flaggedStorage) => {
|
|
1598
1647
|
const previousAccounts = cloneAccountStorageForPersistence(state.snapshot);
|
|
1599
|
-
const
|
|
1648
|
+
const baseline = loadedAccountSnapshots.get(accountStorage);
|
|
1649
|
+
const nextAccounts = cloneAccountStorageForPersistence(baseline ? mergeAccountSnapshot(baseline, state.snapshot, accountStorage) : accountStorage);
|
|
1600
1650
|
const nextFlagged = cloneFlaggedStorageForPersistence(flaggedStorage);
|
|
1601
1651
|
await saveAccountsUnlocked(nextAccounts);
|
|
1602
1652
|
try {
|
|
@@ -1659,11 +1709,29 @@ export async function withFlaggedStorageTransaction(handler) {
|
|
|
1659
1709
|
* @param storage - Account storage data to save
|
|
1660
1710
|
* @throws StorageError with platform-aware hints on failure
|
|
1661
1711
|
*/
|
|
1712
|
+
/** Clone a loaded working snapshot without losing its optimistic-concurrency baseline. */
|
|
1713
|
+
export function cloneTrackedAccountStorage(storage) {
|
|
1714
|
+
const clone = structuredClone(storage);
|
|
1715
|
+
const baseline = loadedAccountSnapshots.get(storage);
|
|
1716
|
+
if (baseline)
|
|
1717
|
+
loadedAccountSnapshots.set(clone, structuredClone(baseline));
|
|
1718
|
+
return clone;
|
|
1719
|
+
}
|
|
1662
1720
|
export async function saveAccounts(storage) {
|
|
1663
1721
|
return saveAccountsEntry({
|
|
1664
1722
|
storage,
|
|
1665
1723
|
withStorageLock,
|
|
1666
|
-
saveUnlocked:
|
|
1724
|
+
saveUnlocked: async (proposed) => {
|
|
1725
|
+
const baseline = loadedAccountSnapshots.get(proposed);
|
|
1726
|
+
const snapshot = baseline ? mergeAccountSnapshot(baseline, await loadPrimaryAccountsForMerge(), proposed) : proposed;
|
|
1727
|
+
await saveAccountsUnlocked(snapshot);
|
|
1728
|
+
if (baseline) {
|
|
1729
|
+
for (const key of Object.keys(proposed))
|
|
1730
|
+
Reflect.deleteProperty(proposed, key);
|
|
1731
|
+
Object.assign(proposed, structuredClone(snapshot));
|
|
1732
|
+
loadedAccountSnapshots.set(proposed, structuredClone(snapshot));
|
|
1733
|
+
}
|
|
1734
|
+
},
|
|
1667
1735
|
});
|
|
1668
1736
|
}
|
|
1669
1737
|
/**
|
|
@@ -1672,7 +1740,7 @@ export async function saveAccounts(storage) {
|
|
|
1672
1740
|
*/
|
|
1673
1741
|
export async function clearAccounts() {
|
|
1674
1742
|
const path = getStoragePath();
|
|
1675
|
-
|
|
1743
|
+
await clearAccountsEntry({
|
|
1676
1744
|
path,
|
|
1677
1745
|
withStorageLock,
|
|
1678
1746
|
resetMarkerPath: getIntentionalResetMarkerPath(path),
|
|
@@ -1683,6 +1751,8 @@ export async function clearAccounts() {
|
|
|
1683
1751
|
log.error(message, details);
|
|
1684
1752
|
},
|
|
1685
1753
|
});
|
|
1754
|
+
// Pending rotated credentials belong to the cleared pool.
|
|
1755
|
+
await clearPendingAuth(path);
|
|
1686
1756
|
}
|
|
1687
1757
|
export async function loadFlaggedAccounts() {
|
|
1688
1758
|
return loadFlaggedAccountsEntry({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth-menu-builder.d.ts","sourceRoot":"","sources":["../../../lib/ui/auth-menu-builder.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"auth-menu-builder.d.ts","sourceRoot":"","sources":["../../../lib/ui/auth-menu-builder.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth-menu.d.ts","sourceRoot":"","sources":["../../../lib/ui/auth-menu.ts"],"names":[],"mappings":"AAEA,OAAO,EAAQ,KAAK,EAAE,MAAM,WAAW,CAAC;AAMxC,OAAO,EAWN,KAAK,aAAa,EAClB,KAAK,WAAW,EAChB,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,MAAM,wBAAwB,CAAC;AAEhC,YAAY,EACX,aAAa,EACb,WAAW,EACX,aAAa,EACb,cAAc,EACd,eAAe,GACf,MAAM,wBAAwB,CAAC;AAiBhC,wBAAsB,YAAY,CACjC,QAAQ,EAAE,WAAW,EAAE,EACvB,OAAO,GAAE,eAAoB,GAC3B,OAAO,CAAC,cAAc,CAAC,
|
|
1
|
+
{"version":3,"file":"auth-menu.d.ts","sourceRoot":"","sources":["../../../lib/ui/auth-menu.ts"],"names":[],"mappings":"AAEA,OAAO,EAAQ,KAAK,EAAE,MAAM,WAAW,CAAC;AAMxC,OAAO,EAWN,KAAK,aAAa,EAClB,KAAK,WAAW,EAChB,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,MAAM,wBAAwB,CAAC;AAEhC,YAAY,EACX,aAAa,EACb,WAAW,EACX,aAAa,EACb,cAAc,EACd,eAAe,GACf,MAAM,wBAAwB,CAAC;AAiBhC,wBAAsB,YAAY,CACjC,QAAQ,EAAE,WAAW,EAAE,EACvB,OAAO,GAAE,eAAoB,GAC3B,OAAO,CAAC,cAAc,CAAC,CA0LzB;AAED,wBAAsB,kBAAkB,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,aAAa,CAAC,CAmErF;AAED,OAAO,EAAE,KAAK,EAAE,CAAC"}
|
package/dist/lib/ui/auth-menu.js
CHANGED
|
@@ -49,6 +49,7 @@ export async function showAuthMenu(accounts, options = {}) {
|
|
|
49
49
|
{ label: UI_COPY.mainMenu.checkAccounts, value: { type: "check" }, color: "green" },
|
|
50
50
|
{ label: UI_COPY.mainMenu.bestAccount, value: { type: "forecast" }, color: "green" },
|
|
51
51
|
{ label: UI_COPY.mainMenu.fixIssues, value: { type: "fix" }, color: "green" },
|
|
52
|
+
{ label: "API credentials and models", value: { type: "api-models" }, color: "green" },
|
|
52
53
|
{ label: UI_COPY.mainMenu.settings, value: { type: "settings" }, color: "green" },
|
|
53
54
|
{ label: "", value: { type: "cancel" }, separator: true },
|
|
54
55
|
{ label: UI_COPY.mainMenu.moreChecks, value: { type: "cancel" }, kind: "heading" },
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
type ProgressOutput = {
|
|
2
|
+
isTTY?: boolean;
|
|
3
|
+
write: (text: string) => unknown;
|
|
4
|
+
};
|
|
5
|
+
/** Indeterminate progress: elapsed time is real; no guessed completion percentage. */
|
|
6
|
+
export declare function withCheckProgress<T>(label: string | (() => string), operation: () => Promise<T>, log?: (message: string) => void, output?: ProgressOutput): Promise<T>;
|
|
7
|
+
export {};
|
|
8
|
+
//# sourceMappingURL=check-progress.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"check-progress.d.ts","sourceRoot":"","sources":["../../../lib/ui/check-progress.ts"],"names":[],"mappings":"AAEA,KAAK,cAAc,GAAG;IAAE,KAAK,CAAC,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAA;CAAE,CAAC;AAE5E,sFAAsF;AACtF,wBAAsB,iBAAiB,CAAC,CAAC,EACxC,KAAK,EAAE,MAAM,GAAG,CAAC,MAAM,MAAM,CAAC,EAC9B,SAAS,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EAC3B,GAAG,GAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAA2D,EACrF,MAAM,GAAE,cAA+B,GACrC,OAAO,CAAC,CAAC,CAAC,CAqBZ"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { ANSI } from "./ansi.js";
|
|
2
|
+
/** Indeterminate progress: elapsed time is real; no guessed completion percentage. */
|
|
3
|
+
export async function withCheckProgress(label, operation, log = message => { process.stdout.write(`${message}\n`); }, output = process.stdout) {
|
|
4
|
+
const message = () => typeof label === "function" ? label() : label;
|
|
5
|
+
const started = Date.now();
|
|
6
|
+
const interactive = output.isTTY === true;
|
|
7
|
+
const frames = ["|", "/", "-", "\\"];
|
|
8
|
+
let frame = 0;
|
|
9
|
+
const elapsed = () => Math.floor((Date.now() - started) / 1000);
|
|
10
|
+
const render = () => {
|
|
11
|
+
if (interactive)
|
|
12
|
+
output.write(`\r${ANSI.clearLine}${frames[frame++ % frames.length]} ${message()} (${elapsed()}s)`);
|
|
13
|
+
else
|
|
14
|
+
log(`${message()}... ${elapsed()}s elapsed`);
|
|
15
|
+
};
|
|
16
|
+
if (interactive)
|
|
17
|
+
render();
|
|
18
|
+
else
|
|
19
|
+
log(`${message()}...`);
|
|
20
|
+
const timer = setInterval(render, interactive ? 120 : 5000);
|
|
21
|
+
timer.unref?.();
|
|
22
|
+
try {
|
|
23
|
+
return await operation();
|
|
24
|
+
}
|
|
25
|
+
finally {
|
|
26
|
+
clearInterval(timer);
|
|
27
|
+
if (interactive)
|
|
28
|
+
output.write(`\r${ANSI.clearLine}`);
|
|
29
|
+
}
|
|
30
|
+
}
|
package/dist/lib/ui/format.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type UiRuntimeOptions } from "./runtime.js";
|
|
2
2
|
export type UiTextTone = "primary" | "heading" | "accent" | "muted" | "success" | "warning" | "danger" | "normal";
|
|
3
3
|
/**
|
|
4
4
|
* Colorize a text string according to the UI theme and tone.
|
|
@@ -59,4 +59,6 @@ export declare function formatUiBadge(ui: UiRuntimeOptions, label: string, tone?
|
|
|
59
59
|
* Token redaction: does not handle or emit sensitive tokens.
|
|
60
60
|
*/
|
|
61
61
|
export declare function quotaToneFromLeftPercent(leftPercent: number): Extract<UiTextTone, "success" | "warning" | "danger">;
|
|
62
|
+
/** Style human-readable reports; JSON callers must bypass this presentation helper. */
|
|
63
|
+
export declare function styleReportText(text: string, tone?: UiTextTone): string;
|
|
62
64
|
//# sourceMappingURL=format.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../../../lib/ui/format.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../../../lib/ui/format.ts"],"names":[],"mappings":"AAAA,OAAO,EAAuB,KAAK,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAG1E,MAAM,MAAM,UAAU,GACnB,SAAS,GACT,SAAS,GACT,QAAQ,GACR,OAAO,GACP,SAAS,GACT,SAAS,GACT,QAAQ,GACR,QAAQ,CAAC;AAuGZ;;;;;;;;;;;;;GAaG;AACH,wBAAgB,WAAW,CAAC,EAAE,EAAE,gBAAgB,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,GAAE,UAAqB,GAAG,MAAM,CAKnG;AAED,wBAAgB,cAAc,CAAC,EAAE,EAAE,gBAAgB,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAO5E;AAED,wBAAgB,eAAe,CAAC,EAAE,EAAE,gBAAgB,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAG7E;AAED,wBAAgB,YAAY,CAC3B,EAAE,EAAE,gBAAgB,EACpB,IAAI,EAAE,MAAM,EACZ,IAAI,GAAE,UAAqB,GACzB,MAAM,CAIR;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,gBAAgB,CAC/B,EAAE,EAAE,gBAAgB,EACpB,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,MAAM,EACb,SAAS,GAAE,UAAqB,GAC9B,MAAM,CAKR;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,aAAa,CAC5B,EAAE,EAAE,gBAAgB,EACpB,KAAK,EAAE,MAAM,EACb,IAAI,GAAE,OAAO,CAAC,UAAU,EAAE,QAAQ,GAAG,SAAS,CAAY,GACxD,MAAM,CAKR;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,wBAAwB,CACvC,WAAW,EAAE,MAAM,GACjB,OAAO,CAAC,UAAU,EAAE,SAAS,GAAG,SAAS,GAAG,QAAQ,CAAC,CAIvD;AAED,uFAAuF;AACvF,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,GAAE,UAAqB,GAAG,MAAM,CAGjF"}
|
package/dist/lib/ui/format.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { getUiRuntimeOptions } from "./runtime.js";
|
|
2
|
+
import { shouldDisableColor } from "./theme.js";
|
|
1
3
|
const TONE_TO_COLOR = {
|
|
2
4
|
primary: "primary",
|
|
3
5
|
heading: "heading",
|
|
@@ -202,3 +204,9 @@ export function quotaToneFromLeftPercent(leftPercent) {
|
|
|
202
204
|
return "warning";
|
|
203
205
|
return "success";
|
|
204
206
|
}
|
|
207
|
+
/** Style human-readable reports; JSON callers must bypass this presentation helper. */
|
|
208
|
+
export function styleReportText(text, tone = "normal") {
|
|
209
|
+
if (shouldDisableColor())
|
|
210
|
+
return text;
|
|
211
|
+
return paintUiText(getUiRuntimeOptions(), text, tone);
|
|
212
|
+
}
|
package/dist/lib/ui/ui-copy.d.ts
CHANGED
|
@@ -143,8 +143,8 @@ export declare const UI_COPY: {
|
|
|
143
143
|
readonly fallback: {
|
|
144
144
|
readonly addAnotherTip: "Tip: Use private mode or sign out before adding another account.";
|
|
145
145
|
readonly addAnotherQuestion: (count: number) => string;
|
|
146
|
-
readonly selectModePrompt: "(a) add, (c) check, (b) best, fi(x), (s) settings, (d) deep, (g) problem, (f) fresh, (q) back [a/c/b/x/s/d/g/f/q]: ";
|
|
147
|
-
readonly invalidModePrompt: "Use one of: a, c, b, x, s, d, g, f, q.";
|
|
146
|
+
readonly selectModePrompt: "(api) API models, (a) add, (c) check, (b) best, fi(x), (s) settings, (d) deep, (g) problem, (f) fresh, (q) back [api/a/c/b/x/s/d/g/f/q]: ";
|
|
147
|
+
readonly invalidModePrompt: "Use one of: api, a, c, b, x, s, d, g, f, q.";
|
|
148
148
|
};
|
|
149
149
|
};
|
|
150
150
|
/**
|