oc-codex-multi-auth 6.3.1 → 6.3.3
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 +89 -2
- package/dist/index.js.map +1 -1
- package/dist/lib/error-sentinels.d.ts +26 -0
- package/dist/lib/error-sentinels.d.ts.map +1 -1
- package/dist/lib/error-sentinels.js +30 -0
- package/dist/lib/error-sentinels.js.map +1 -1
- package/dist/lib/request/fetch-helpers.d.ts +15 -0
- package/dist/lib/request/fetch-helpers.d.ts.map +1 -1
- package/dist/lib/request/fetch-helpers.js +54 -1
- package/dist/lib/request/fetch-helpers.js.map +1 -1
- package/dist/lib/request/helpers/model-map.d.ts.map +1 -1
- package/dist/lib/request/helpers/model-map.js +23 -18
- package/dist/lib/request/helpers/model-map.js.map +1 -1
- package/dist/lib/request/request-transformer.d.ts.map +1 -1
- package/dist/lib/request/request-transformer.js +7 -6
- package/dist/lib/request/request-transformer.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAKH,OAAO,KAAK,EAAE,MAAM,EAAe,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAKH,OAAO,KAAK,EAAE,MAAM,EAAe,MAAM,qBAAqB,CAAC;AAwM/D;;;;;;;;;;;;;;;GAeG;AAEH,eAAO,MAAM,iBAAiB,EAAE,MAyqH/B,CAAC;AAEF,eAAO,MAAM,gBAAgB,QAAoB,CAAC;AAElD,eAAe,iBAAiB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -41,8 +41,8 @@ import { AccountManager, extractAccountEmail, extractAccountId, formatAccountLab
|
|
|
41
41
|
import { resolveDisplayEmail } from "./lib/account-display.js";
|
|
42
42
|
import { CodexAuthError } from "./lib/errors.js";
|
|
43
43
|
import { getStoragePath, loadAccounts, saveAccounts, withAccountStorageTransaction, clearAccounts, setStoragePath, loadFlaggedAccounts, saveFlaggedAccounts, withFlaggedAccountStorageTransaction, clearFlaggedAccounts, getWorkspaceIdentityKey, StorageError, formatStorageErrorHint, } from "./lib/storage.js";
|
|
44
|
-
import { createCodexHeaders, extractRequestUrl, handleErrorResponse, handleSuccessResponse, isDeactivatedWorkspaceError, getUnsupportedCodexModelInfo, resolveUnsupportedCodexFallbackModel, refreshAndUpdateToken, rewriteUrlForCodex, shouldRefreshToken, transformRequestForCodex, } from "./lib/request/fetch-helpers.js";
|
|
45
|
-
import { createDeactivatedWorkspaceError, DEACTIVATED_WORKSPACE_ERROR_CODE, isDeactivatedWorkspaceErrorMessage, } from "./lib/error-sentinels.js";
|
|
44
|
+
import { createCodexHeaders, extractRequestUrl, handleErrorResponse, handleSuccessResponse, isDeactivatedWorkspaceError, isInvalidatedAuthTokenError, getUnsupportedCodexModelInfo, resolveUnsupportedCodexFallbackModel, refreshAndUpdateToken, rewriteUrlForCodex, shouldRefreshToken, transformRequestForCodex, } from "./lib/request/fetch-helpers.js";
|
|
45
|
+
import { createDeactivatedWorkspaceError, DEACTIVATED_WORKSPACE_ERROR_CODE, isDeactivatedWorkspaceErrorMessage, isInvalidatedAuthTokenMessage, } from "./lib/error-sentinels.js";
|
|
46
46
|
import { applyFastSessionDefaults, upsertBackendModelIdentityMessage, } from "./lib/request/request-transformer.js";
|
|
47
47
|
import { getRateLimitBackoff, RATE_LIMIT_SHORT_RETRY_THRESHOLD_MS, resetRateLimitBackoff, } from "./lib/request/rate-limit-backoff.js";
|
|
48
48
|
import { isEmptyResponse } from "./lib/request/response-handler.js";
|
|
@@ -1861,6 +1861,63 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
1861
1861
|
}
|
|
1862
1862
|
break;
|
|
1863
1863
|
}
|
|
1864
|
+
// A 401 token-invalidated response means the access token presented for
|
|
1865
|
+
// THIS account was rejected by the backend even though the proactive
|
|
1866
|
+
// refresh above either ran or judged the token still fresh. Without an
|
|
1867
|
+
// explicit handler the 401 fell straight through to `return errorResponse`
|
|
1868
|
+
// below, so persisted family routing kept pinning every request to the dead
|
|
1869
|
+
// account slot instead of failing over (issue #171). Treat it as an
|
|
1870
|
+
// account-health failure: cool the refresh-token group down (or remove it
|
|
1871
|
+
// past the failure threshold) and rotate to the next healthy account.
|
|
1872
|
+
//
|
|
1873
|
+
// Note: 401s intentionally do NOT feed the circuit breaker — the breaker
|
|
1874
|
+
// guards against upstream faults (network / 5xx), not client-side auth
|
|
1875
|
+
// decisions (see the 5xx handler above).
|
|
1876
|
+
if (isInvalidatedAuthTokenError(errorBody, response.status)) {
|
|
1877
|
+
const accountLabel = formatAccountLabel(account, account.index, {
|
|
1878
|
+
maskEmail: maskEmailEnabled,
|
|
1879
|
+
});
|
|
1880
|
+
accountManager.refundToken(account, modelFamily, model);
|
|
1881
|
+
accountManager.recordFailure(account, modelFamily, model);
|
|
1882
|
+
account.lastSwitchReason = "rotation";
|
|
1883
|
+
runtimeMetrics.failedRequests++;
|
|
1884
|
+
runtimeMetrics.accountRotations++;
|
|
1885
|
+
runtimeMetrics.lastError = `Auth token invalidated on ${accountLabel}`;
|
|
1886
|
+
runtimeMetrics.lastErrorCategory = "auth-invalidated";
|
|
1887
|
+
const failures = await accountManager.incrementAuthFailures(account);
|
|
1888
|
+
if (failures >= ACCOUNT_LIMITS.MAX_AUTH_FAILURES_BEFORE_REMOVAL) {
|
|
1889
|
+
const removedCount = accountManager.removeAccountsWithSameRefreshToken(account);
|
|
1890
|
+
if (removedCount > 0) {
|
|
1891
|
+
accountManager.saveToDiskDebounced();
|
|
1892
|
+
await showToast(removedCount > 1
|
|
1893
|
+
? `Removed ${removedCount} accounts (same refresh token) after ${failures} auth-token failures. Run 'opencode auth login' to re-add.`
|
|
1894
|
+
: `Removed ${accountLabel} after ${failures} auth-token failures. Run 'opencode auth login' to re-add.`, "error", { duration: toastDurationMs * 2 });
|
|
1895
|
+
// Indices shift after removal; restart traversal with a fresh
|
|
1896
|
+
// attempted set so no healthy account is skipped.
|
|
1897
|
+
attempted.clear();
|
|
1898
|
+
accountCount = accountManager.getAccountCount();
|
|
1899
|
+
break;
|
|
1900
|
+
}
|
|
1901
|
+
logWarn(`[${PLUGIN_NAME}] Expected grouped account removal after auth-token invalidation, but removed ${removedCount}.`);
|
|
1902
|
+
}
|
|
1903
|
+
// Below the removal threshold (or grouped removal was a no-op): cool the
|
|
1904
|
+
// account's whole refresh-token group down so selection skips it, then
|
|
1905
|
+
// rotate to the next healthy account instead of returning the 401.
|
|
1906
|
+
const cooledCount = accountManager.markAccountsWithRefreshTokenCoolingDown(account.refreshToken, ACCOUNT_LIMITS.AUTH_FAILURE_COOLDOWN_MS, "auth-failure");
|
|
1907
|
+
if (cooledCount <= 0) {
|
|
1908
|
+
accountManager.markAccountCoolingDown(account, ACCOUNT_LIMITS.AUTH_FAILURE_COOLDOWN_MS, "auth-failure");
|
|
1909
|
+
}
|
|
1910
|
+
accountManager.saveToDiskDebounced();
|
|
1911
|
+
logWarn(accountCount > 1
|
|
1912
|
+
? `Auth token invalidated for account ${account.index + 1}. Cooling down and rotating to next account.`
|
|
1913
|
+
: `Auth token invalidated for account ${account.index + 1}. Cooling down; no other account available.`);
|
|
1914
|
+
if (accountCount > 1 &&
|
|
1915
|
+
accountManager.shouldShowAccountToast(account.index, rateLimitToastDebounceMs)) {
|
|
1916
|
+
await showToast(`Account ${account.index + 1} sign-in expired. Switching accounts.`, "warning", { duration: toastDurationMs });
|
|
1917
|
+
accountManager.markToastShown(account.index);
|
|
1918
|
+
}
|
|
1919
|
+
break;
|
|
1920
|
+
}
|
|
1864
1921
|
runtimeMetrics.failedRequests++;
|
|
1865
1922
|
runtimeMetrics.lastError = `HTTP ${response.status}`;
|
|
1866
1923
|
runtimeMetrics.lastErrorCategory = "http";
|
|
@@ -1927,6 +1984,12 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
1927
1984
|
}
|
|
1928
1985
|
}
|
|
1929
1986
|
accountManager.recordSuccess(account, modelFamily, model);
|
|
1987
|
+
// A successful request proves the account's credentials are good
|
|
1988
|
+
// again, so reset the auth-failure counter that a prior 401
|
|
1989
|
+
// token-invalidated response (or refresh failure) may have bumped.
|
|
1990
|
+
// Otherwise stale counts could accumulate across requests and
|
|
1991
|
+
// eventually remove a now-healthy account.
|
|
1992
|
+
accountManager.clearAuthFailures(account);
|
|
1930
1993
|
// RC-8: closes a half-open gate or prunes the failure window so a
|
|
1931
1994
|
// sequence of successes keeps the breaker healthy.
|
|
1932
1995
|
circuitBreaker.recordSuccess();
|
|
@@ -2248,6 +2311,15 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
2248
2311
|
if (isDeactivatedWorkspaceError(errorBody, response.status)) {
|
|
2249
2312
|
throw createDeactivatedWorkspaceError();
|
|
2250
2313
|
}
|
|
2314
|
+
// A 401 here proves the token is invalid even when the body
|
|
2315
|
+
// carries only a generic "Unauthorized" string. The status is
|
|
2316
|
+
// dropped once we throw a plain Error, so normalize to the
|
|
2317
|
+
// canonical message the catch below matches — otherwise a
|
|
2318
|
+
// non-specific 401 would leave a dead routing slot unflagged
|
|
2319
|
+
// for codex-doctor --fix (issue #171).
|
|
2320
|
+
if (isInvalidatedAuthTokenError(errorBody, response.status)) {
|
|
2321
|
+
throw new Error("Your authentication token has been invalidated. Please try signing in again.");
|
|
2322
|
+
}
|
|
2251
2323
|
throw new Error(message);
|
|
2252
2324
|
}
|
|
2253
2325
|
lastError = new Error("Codex response did not include quota headers");
|
|
@@ -2450,6 +2522,21 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
2450
2522
|
removeFromActive.add(getWorkspaceIdentityKey(account));
|
|
2451
2523
|
flaggedChanged = true;
|
|
2452
2524
|
}
|
|
2525
|
+
else if (isInvalidatedAuthTokenMessage(message)) {
|
|
2526
|
+
// The cached access token probed OK locally but the backend
|
|
2527
|
+
// rejected it (401 invalidated). Surface it so `codex-doctor
|
|
2528
|
+
// --fix` repairs the active routing instead of leaving a dead
|
|
2529
|
+
// slot selected (issue #171).
|
|
2530
|
+
const flaggedRecord = {
|
|
2531
|
+
...account,
|
|
2532
|
+
flaggedAt: Date.now(),
|
|
2533
|
+
flaggedReason: "token-invalid",
|
|
2534
|
+
lastError: message,
|
|
2535
|
+
};
|
|
2536
|
+
flaggedUpdates.set(getWorkspaceIdentityKey(flaggedRecord), flaggedRecord);
|
|
2537
|
+
removeFromActive.add(getWorkspaceIdentityKey(account));
|
|
2538
|
+
flaggedChanged = true;
|
|
2539
|
+
}
|
|
2453
2540
|
console.log(`[${i + 1}/${total}] ${label}: ERROR (${message.slice(0, 160)})`);
|
|
2454
2541
|
}
|
|
2455
2542
|
}
|