oc-codex-multi-auth 6.19.0 → 6.20.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/dist/index.d.ts.map +1 -1
- package/dist/index.js +180 -79
- package/dist/index.js.map +1 -1
- package/dist/lib/accounts/persistence.d.ts +22 -0
- package/dist/lib/accounts/persistence.d.ts.map +1 -1
- package/dist/lib/accounts/persistence.js +79 -0
- package/dist/lib/accounts/persistence.js.map +1 -1
- package/dist/lib/accounts/rate-limits.d.ts +22 -0
- package/dist/lib/accounts/rate-limits.d.ts.map +1 -1
- package/dist/lib/accounts/rate-limits.js +25 -0
- package/dist/lib/accounts/rate-limits.js.map +1 -1
- package/dist/lib/accounts/rotation.d.ts +22 -23
- package/dist/lib/accounts/rotation.d.ts.map +1 -1
- package/dist/lib/accounts/rotation.js +62 -37
- package/dist/lib/accounts/rotation.js.map +1 -1
- package/dist/lib/accounts/stale-state.d.ts +31 -0
- package/dist/lib/accounts/stale-state.d.ts.map +1 -1
- package/dist/lib/accounts/stale-state.js +56 -1
- package/dist/lib/accounts/stale-state.js.map +1 -1
- package/dist/lib/accounts/state.d.ts +6 -0
- package/dist/lib/accounts/state.d.ts.map +1 -1
- package/dist/lib/accounts/state.js +15 -1
- package/dist/lib/accounts/state.js.map +1 -1
- package/dist/lib/auth/login-runner.d.ts +3 -0
- package/dist/lib/auth/login-runner.d.ts.map +1 -1
- package/dist/lib/auth/login-runner.js +35 -0
- package/dist/lib/auth/login-runner.js.map +1 -1
- package/dist/lib/codex-usage.d.ts +13 -5
- package/dist/lib/codex-usage.d.ts.map +1 -1
- package/dist/lib/codex-usage.js +33 -21
- package/dist/lib/codex-usage.js.map +1 -1
- package/dist/lib/parallel-probe.d.ts.map +1 -1
- package/dist/lib/parallel-probe.js +4 -2
- package/dist/lib/parallel-probe.js.map +1 -1
- package/dist/lib/request/fetch-helpers.d.ts +24 -0
- package/dist/lib/request/fetch-helpers.d.ts.map +1 -1
- package/dist/lib/request/fetch-helpers.js +66 -20
- package/dist/lib/request/fetch-helpers.js.map +1 -1
- package/dist/lib/schemas.d.ts +9 -0
- package/dist/lib/schemas.d.ts.map +1 -1
- package/dist/lib/schemas.js +3 -0
- package/dist/lib/schemas.js.map +1 -1
- package/dist/lib/storage/flagged.d.ts.map +1 -1
- package/dist/lib/storage/flagged.js +5 -0
- package/dist/lib/storage/flagged.js.map +1 -1
- package/dist/lib/storage/migrations.d.ts +21 -0
- package/dist/lib/storage/migrations.d.ts.map +1 -1
- package/dist/lib/storage/migrations.js.map +1 -1
- package/dist/lib/storage/normalize.d.ts.map +1 -1
- package/dist/lib/storage/normalize.js +57 -1
- package/dist/lib/storage/normalize.js.map +1 -1
- package/dist/lib/tools/codex-doctor.d.ts.map +1 -1
- package/dist/lib/tools/codex-doctor.js +8 -66
- package/dist/lib/tools/codex-doctor.js.map +1 -1
- package/dist/lib/tools/codex-health.d.ts.map +1 -1
- package/dist/lib/tools/codex-health.js +7 -1
- package/dist/lib/tools/codex-health.js.map +1 -1
- package/dist/lib/tools/codex-list.d.ts.map +1 -1
- package/dist/lib/tools/codex-list.js +14 -1
- package/dist/lib/tools/codex-list.js.map +1 -1
- package/dist/lib/tools/codex-status.d.ts.map +1 -1
- package/dist/lib/tools/codex-status.js +6 -1
- package/dist/lib/tools/codex-status.js.map +1 -1
- package/dist/lib/tools/doctor-repair.d.ts +10 -0
- package/dist/lib/tools/doctor-repair.d.ts.map +1 -0
- package/dist/lib/tools/doctor-repair.js +86 -0
- package/dist/lib/tools/doctor-repair.js.map +1 -0
- package/dist/lib/tools/index.d.ts +3 -0
- package/dist/lib/tools/index.d.ts.map +1 -1
- package/dist/lib/tools/index.js.map +1 -1
- package/package.json +1 -1
- package/scripts/install-oc-codex-multi-auth-core.js +133 -6
|
@@ -257,8 +257,28 @@ async function readStandaloneStorage(path) {
|
|
|
257
257
|
try {
|
|
258
258
|
const raw = await readFile(path, "utf-8");
|
|
259
259
|
const parsed = JSON.parse(raw);
|
|
260
|
+
// Shape validation, not just parse validation: a JSON array, scalar, or
|
|
261
|
+
// object without an `accounts` array is unreadable by the plugin runtime
|
|
262
|
+
// too (normalizeAccountStorage rejects it), so reporting it as a healthy
|
|
263
|
+
// empty pool (exit 0, "No accounts configured") hides the corruption
|
|
264
|
+
// from scripted callers that key on exit codes.
|
|
265
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
266
|
+
return { storage: null, error: "Storage file must be a JSON object with an accounts array." };
|
|
267
|
+
}
|
|
268
|
+
// Forward-compat mirror of the runtime guard: a newer schema version
|
|
269
|
+
// must not be shown as readable accounts by this build.
|
|
270
|
+
const version = parsed.version;
|
|
271
|
+
if (typeof version === "number" && Number.isFinite(version) && version > 3) {
|
|
272
|
+
return {
|
|
273
|
+
storage: null,
|
|
274
|
+
error: `Unsupported account storage schema version ${version}; this build supports up to version 3.`,
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
if (!Array.isArray(parsed.accounts)) {
|
|
278
|
+
return { storage: null, error: "Storage file must be a JSON object with an accounts array." };
|
|
279
|
+
}
|
|
260
280
|
return {
|
|
261
|
-
storage:
|
|
281
|
+
storage: normalizeStandaloneStorage(parsed),
|
|
262
282
|
error: null,
|
|
263
283
|
};
|
|
264
284
|
} catch (error) {
|
|
@@ -417,6 +437,7 @@ function summarizeStandaloneAccounts(storage, includeSensitive, tag) {
|
|
|
417
437
|
tags: Array.isArray(account?.accountTags) ? account.accountTags : [],
|
|
418
438
|
note: account?.accountNote,
|
|
419
439
|
rateLimitResetTimes: account?.rateLimitResetTimes ?? {},
|
|
440
|
+
quotaExhaustedUntil: account?.quotaExhaustedUntil,
|
|
420
441
|
};
|
|
421
442
|
});
|
|
422
443
|
}
|
|
@@ -440,6 +461,8 @@ function printStandaloneResult(command, payload, json) {
|
|
|
440
461
|
}
|
|
441
462
|
}
|
|
442
463
|
if (payload.error) console.log(`Error: ${payload.error}`);
|
|
464
|
+
for (const fix of payload.appliedFixes ?? []) console.log(`Fixed: ${fix}`);
|
|
465
|
+
for (const error of payload.fixErrors ?? []) console.log(`Repair failed: ${error}`);
|
|
443
466
|
if (payload.nextAction) console.log(`Next: ${payload.nextAction}`);
|
|
444
467
|
}
|
|
445
468
|
|
|
@@ -509,9 +532,29 @@ export async function runWarmCommand(parsed, options = {}) {
|
|
|
509
532
|
// persisted to the SAME file the rest of the toolchain reads.
|
|
510
533
|
storageMod.setStoragePathDirect(storagePath);
|
|
511
534
|
|
|
512
|
-
|
|
535
|
+
let storage = null;
|
|
536
|
+
try {
|
|
537
|
+
storage = await storageMod.loadAccounts();
|
|
538
|
+
} catch (error) {
|
|
539
|
+
// Typed storage errors (e.g. UNSUPPORTED_SCHEMA_VERSION) carry the
|
|
540
|
+
// upgrade hint; surface them rather than crashing the CLI.
|
|
541
|
+
const hint = error && typeof error.hint === "string" ? ` ${error.hint}` : "";
|
|
542
|
+
const payload = { command: "warm", storagePath, error: `${formatErrorForLog(error)}${hint}` };
|
|
543
|
+
printWarmResult(payload, parsed.json);
|
|
544
|
+
return { exitCode: 1, action: "warm", storagePath };
|
|
545
|
+
}
|
|
513
546
|
const accounts = Array.isArray(storage?.accounts) ? storage.accounts : [];
|
|
514
547
|
if (accounts.length === 0) {
|
|
548
|
+
// `loadAccounts` swallows parse/IO errors and returns null. Probe the
|
|
549
|
+
// file so a corrupt storage fails like `status`/`doctor` do (exit 1)
|
|
550
|
+
// instead of reporting a healthy empty pool. ENOENT stays a silent
|
|
551
|
+
// empty pool: a missing file legitimately means no accounts yet.
|
|
552
|
+
const probe = await readStandaloneStorage(storagePath);
|
|
553
|
+
if (probe.error) {
|
|
554
|
+
const payload = { command: "warm", storagePath, error: probe.error };
|
|
555
|
+
printWarmResult(payload, parsed.json);
|
|
556
|
+
return { exitCode: 1, action: "warm", storagePath };
|
|
557
|
+
}
|
|
515
558
|
const payload = {
|
|
516
559
|
command: "warm",
|
|
517
560
|
storagePath,
|
|
@@ -615,9 +658,27 @@ export async function runLimitsCommand(parsed, options = {}) {
|
|
|
615
658
|
// persisted to the SAME file the rest of the toolchain reads.
|
|
616
659
|
storageMod.setStoragePathDirect(storagePath);
|
|
617
660
|
|
|
618
|
-
|
|
661
|
+
let storage = null;
|
|
662
|
+
try {
|
|
663
|
+
storage = await storageMod.loadAccounts();
|
|
664
|
+
} catch (error) {
|
|
665
|
+
// Typed storage errors (e.g. UNSUPPORTED_SCHEMA_VERSION) carry the
|
|
666
|
+
// upgrade hint; surface them rather than crashing the CLI.
|
|
667
|
+
const hint = error && typeof error.hint === "string" ? ` ${error.hint}` : "";
|
|
668
|
+
const payload = { command: "limits", storagePath, error: `${formatErrorForLog(error)}${hint}` };
|
|
669
|
+
printLimitsResult(payload, parsed.json);
|
|
670
|
+
return { exitCode: 1, action: "limits", storagePath };
|
|
671
|
+
}
|
|
619
672
|
const accounts = Array.isArray(storage?.accounts) ? storage.accounts : [];
|
|
620
673
|
if (accounts.length === 0) {
|
|
674
|
+
// Same probe contract as `warm`: a corrupt file exits 1 like
|
|
675
|
+
// `status`/`doctor`; a missing file stays a silent empty pool.
|
|
676
|
+
const probe = await readStandaloneStorage(storagePath);
|
|
677
|
+
if (probe.error) {
|
|
678
|
+
const payload = { command: "limits", storagePath, error: probe.error };
|
|
679
|
+
printLimitsResult(payload, parsed.json);
|
|
680
|
+
return { exitCode: 1, action: "limits", storagePath };
|
|
681
|
+
}
|
|
621
682
|
const payload = {
|
|
622
683
|
command: "limits",
|
|
623
684
|
storagePath,
|
|
@@ -658,6 +719,7 @@ export async function runLimitsCommand(parsed, options = {}) {
|
|
|
658
719
|
label: account.accountLabel ?? `Account ${index + 1}`,
|
|
659
720
|
email: maskValue(account.email, parsed.includeSensitive),
|
|
660
721
|
rateLimitResetTimes: account.rateLimitResetTimes ?? {},
|
|
722
|
+
quotaExhaustedUntil: account.quotaExhaustedUntil,
|
|
661
723
|
};
|
|
662
724
|
try {
|
|
663
725
|
const { accessToken } = await usageMod.ensureCodexUsageAccessToken({ storage, account });
|
|
@@ -771,7 +833,68 @@ export async function runStandaloneCommand(command, argv = [], options = {}) {
|
|
|
771
833
|
}
|
|
772
834
|
const { env = process.env } = options;
|
|
773
835
|
const storagePath = getStandaloneStoragePath(parsed, env);
|
|
774
|
-
const
|
|
836
|
+
const repairRequested = command === "doctor" && parsed.fix;
|
|
837
|
+
let storage = null;
|
|
838
|
+
let error = null;
|
|
839
|
+
if (parsed.configPath || !repairRequested) {
|
|
840
|
+
({ storage, error } = await readStandaloneStorage(storagePath));
|
|
841
|
+
}
|
|
842
|
+
const appliedFixes = [];
|
|
843
|
+
const fixErrors = [];
|
|
844
|
+
if (repairRequested && !error) {
|
|
845
|
+
const previousKeychain = process.env.CODEX_KEYCHAIN;
|
|
846
|
+
try {
|
|
847
|
+
const loadDoctorRuntime = options.loadDoctorRuntime ?? (() => loadDistModules(
|
|
848
|
+
["storage.js", "tools/doctor-repair.js", "shutdown.js"], "doctor",
|
|
849
|
+
));
|
|
850
|
+
const [storageMod, repairMod, shutdownMod] = await loadDoctorRuntime();
|
|
851
|
+
// A CLI file selection must not read or replace the global keychain pool.
|
|
852
|
+
if (parsed.configPath) process.env.CODEX_KEYCHAIN = "0";
|
|
853
|
+
storageMod.setStoragePathDirect(storagePath);
|
|
854
|
+
shutdownMod.setShutdownOwnsProcess(true);
|
|
855
|
+
try {
|
|
856
|
+
storage = await storageMod.loadAccounts();
|
|
857
|
+
} catch (loadError) {
|
|
858
|
+
// Typed storage errors (UNSUPPORTED_SCHEMA_VERSION, unknown V2)
|
|
859
|
+
// carry exact in-tree copy plus an upgrade/recovery hint, and the
|
|
860
|
+
// load never got far enough to attempt a repair. Surface them on
|
|
861
|
+
// the error channel (exit 1) instead of the generic catch below,
|
|
862
|
+
// which is reserved for unknown throws so upstream failure text
|
|
863
|
+
// never reaches output unredacted.
|
|
864
|
+
if (loadError && typeof loadError.code === "string") {
|
|
865
|
+
const hint = typeof loadError.hint === "string" ? ` ${loadError.hint}` : "";
|
|
866
|
+
error = `${formatErrorForLog(loadError)}${hint}`;
|
|
867
|
+
} else {
|
|
868
|
+
throw loadError;
|
|
869
|
+
}
|
|
870
|
+
}
|
|
871
|
+
if (!storage && !error) {
|
|
872
|
+
// `loadAccounts` swallows JSON parse/IO errors and returns null. In
|
|
873
|
+
// default-path mode the pre-read above was skipped (keychain routing
|
|
874
|
+
// may own the pool), so probe the JSON file here: a corrupt file must
|
|
875
|
+
// surface as a parse error (exit 1) instead of "No accounts
|
|
876
|
+
// configured" (exit 0). ENOENT stays silent - a missing file with an
|
|
877
|
+
// empty keychain legitimately means no accounts yet. Skipped when a
|
|
878
|
+
// typed load error already set `error`; the probe would only
|
|
879
|
+
// overwrite the precise schema message with its own paraphrase.
|
|
880
|
+
const probe = await readStandaloneStorage(storagePath);
|
|
881
|
+
if (probe.error) error = probe.error;
|
|
882
|
+
}
|
|
883
|
+
if (!error) {
|
|
884
|
+
const repair = await repairMod.repairDoctorAccounts(storage?.accounts ?? []);
|
|
885
|
+
appliedFixes.push(...repair.appliedFixes);
|
|
886
|
+
fixErrors.push(...repair.fixErrors);
|
|
887
|
+
storage = (await storageMod.loadAccounts()) ?? storage;
|
|
888
|
+
}
|
|
889
|
+
} catch {
|
|
890
|
+
fixErrors.push("Doctor repair could not complete. Check the selected storage file and installed runtime.");
|
|
891
|
+
} finally {
|
|
892
|
+
if (parsed.configPath) {
|
|
893
|
+
if (previousKeychain === undefined) delete process.env.CODEX_KEYCHAIN;
|
|
894
|
+
else process.env.CODEX_KEYCHAIN = previousKeychain;
|
|
895
|
+
}
|
|
896
|
+
}
|
|
897
|
+
}
|
|
775
898
|
const accounts = summarizeStandaloneAccounts(storage, parsed.includeSensitive, parsed.tag);
|
|
776
899
|
const totalAccounts = Array.isArray(storage?.accounts) ? storage.accounts.length : 0;
|
|
777
900
|
const payload = {
|
|
@@ -790,7 +913,11 @@ export async function runStandaloneCommand(command, argv = [], options = {}) {
|
|
|
790
913
|
} else if (command === "doctor") {
|
|
791
914
|
payload.message = error ? "Storage could not be parsed." : totalAccounts > 0 ? "Local diagnostics completed." : "No accounts configured.";
|
|
792
915
|
payload.deep = parsed.deep;
|
|
793
|
-
payload.fixApplied = parsed.fix ?
|
|
916
|
+
payload.fixApplied = parsed.fix ? appliedFixes.length > 0 : undefined;
|
|
917
|
+
if (parsed.fix) {
|
|
918
|
+
payload.appliedFixes = appliedFixes;
|
|
919
|
+
payload.fixErrors = fixErrors;
|
|
920
|
+
}
|
|
794
921
|
payload.nextAction = totalAccounts > 0 ? "Run oc-codex-multi-auth health --json for scriptable checks." : "Run opencode auth login.";
|
|
795
922
|
} else if (command === "health") {
|
|
796
923
|
payload.healthyCount = accounts.filter((account) => account.enabled && account.hasRefreshToken).length;
|
|
@@ -799,7 +926,7 @@ export async function runStandaloneCommand(command, argv = [], options = {}) {
|
|
|
799
926
|
payload.message = totalAccounts > 0 ? "Account storage loaded." : "No accounts configured.";
|
|
800
927
|
}
|
|
801
928
|
printStandaloneResult(command, payload, parsed.json);
|
|
802
|
-
return { exitCode: error ? 1 : 0, action: command, storagePath };
|
|
929
|
+
return { exitCode: error || fixErrors.length > 0 ? 1 : 0, action: command, storagePath };
|
|
803
930
|
}
|
|
804
931
|
|
|
805
932
|
// Top-level keys inside `provider.openai` that the installer owns absolutely.
|