opencode-codex-multi-account 0.2.2 → 0.2.4
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.js +179 -107
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -84,6 +84,23 @@ var PluginConfigSchema = v.object({
|
|
|
84
84
|
quiet_mode: v.optional(v.boolean(), false),
|
|
85
85
|
debug: v.optional(v.boolean(), false)
|
|
86
86
|
});
|
|
87
|
+
var TokenRefreshError = class _TokenRefreshError extends Error {
|
|
88
|
+
status;
|
|
89
|
+
permanent;
|
|
90
|
+
constructor(permanent, status) {
|
|
91
|
+
super(status === void 0 ? "Token refresh failed" : `Token refresh failed: ${status}`);
|
|
92
|
+
this.name = "TokenRefreshError";
|
|
93
|
+
this.status = status;
|
|
94
|
+
this.permanent = permanent;
|
|
95
|
+
Object.setPrototypeOf(this, _TokenRefreshError.prototype);
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
function isTokenRefreshError(error) {
|
|
99
|
+
if (error instanceof TokenRefreshError) return true;
|
|
100
|
+
if (!(error instanceof Error)) return false;
|
|
101
|
+
const candidate = error;
|
|
102
|
+
return candidate.name === "TokenRefreshError" && typeof candidate.permanent === "boolean" && (candidate.status === void 0 || typeof candidate.status === "number");
|
|
103
|
+
}
|
|
87
104
|
|
|
88
105
|
// ../multi-account-core/src/config.ts
|
|
89
106
|
var DEFAULT_CONFIG_FILENAME = "multiauth-config.json";
|
|
@@ -224,6 +241,14 @@ function createMinimalClient() {
|
|
|
224
241
|
}
|
|
225
242
|
};
|
|
226
243
|
}
|
|
244
|
+
function getClearedOAuthBody() {
|
|
245
|
+
return {
|
|
246
|
+
type: "oauth",
|
|
247
|
+
refresh: "",
|
|
248
|
+
access: "",
|
|
249
|
+
expires: 0
|
|
250
|
+
};
|
|
251
|
+
}
|
|
227
252
|
|
|
228
253
|
// ../multi-account-core/src/claims.ts
|
|
229
254
|
var CLAIMS_FILENAME = "multiauth-claims.json";
|
|
@@ -647,13 +672,7 @@ function createAccountManagerForProvider(dependencies) {
|
|
|
647
672
|
});
|
|
648
673
|
}
|
|
649
674
|
async markRevoked(uuid) {
|
|
650
|
-
await this.
|
|
651
|
-
account.isAuthDisabled = true;
|
|
652
|
-
account.authDisabledReason = "OAuth token revoked (403)";
|
|
653
|
-
account.accessToken = void 0;
|
|
654
|
-
account.expiresAt = void 0;
|
|
655
|
-
});
|
|
656
|
-
this.runtimeFactory?.invalidate(uuid);
|
|
675
|
+
await this.removeAccountByUuid(uuid);
|
|
657
676
|
}
|
|
658
677
|
async markSuccess(uuid) {
|
|
659
678
|
this.last429Map.delete(uuid);
|
|
@@ -676,15 +695,32 @@ function createAccountManagerForProvider(dependencies) {
|
|
|
676
695
|
}).catch(() => {
|
|
677
696
|
});
|
|
678
697
|
}
|
|
698
|
+
async clearOpenCodeAuthIfNoAccountsRemain() {
|
|
699
|
+
if (!this.client) return;
|
|
700
|
+
const storage = await this.store.load();
|
|
701
|
+
if (storage.accounts.length > 0) return;
|
|
702
|
+
await this.client.auth.set({
|
|
703
|
+
path: { id: providerAuthId },
|
|
704
|
+
body: getClearedOAuthBody()
|
|
705
|
+
}).catch(() => {
|
|
706
|
+
});
|
|
707
|
+
}
|
|
708
|
+
async removeAccountByUuid(uuid) {
|
|
709
|
+
const removed = await this.store.removeAccount(uuid);
|
|
710
|
+
if (!removed) return;
|
|
711
|
+
this.last429Map.delete(uuid);
|
|
712
|
+
this.runtimeFactory?.invalidate(uuid);
|
|
713
|
+
await this.refresh();
|
|
714
|
+
await this.clearOpenCodeAuthIfNoAccountsRemain();
|
|
715
|
+
}
|
|
679
716
|
async markAuthFailure(uuid, result) {
|
|
717
|
+
if (!result.ok && result.permanent) {
|
|
718
|
+
await this.removeAccountByUuid(uuid);
|
|
719
|
+
return;
|
|
720
|
+
}
|
|
680
721
|
await this.store.mutateStorage((storage) => {
|
|
681
722
|
const account = storage.accounts.find((entry) => entry.uuid === uuid);
|
|
682
723
|
if (!account) return;
|
|
683
|
-
if (!result.ok && result.permanent) {
|
|
684
|
-
account.isAuthDisabled = true;
|
|
685
|
-
account.authDisabledReason = "Token permanently rejected (400/401/403)";
|
|
686
|
-
return;
|
|
687
|
-
}
|
|
688
724
|
account.consecutiveAuthFailures = (account.consecutiveAuthFailures ?? 0) + 1;
|
|
689
725
|
const maxFailures = getConfig().max_consecutive_auth_failures;
|
|
690
726
|
const usableCount = storage.accounts.filter(
|
|
@@ -781,11 +817,21 @@ function createAccountManagerForProvider(dependencies) {
|
|
|
781
817
|
this.cached = [];
|
|
782
818
|
this.activeAccountUuid = void 0;
|
|
783
819
|
}
|
|
784
|
-
async addAccount(auth) {
|
|
820
|
+
async addAccount(auth, email) {
|
|
785
821
|
if (!auth.refresh) return;
|
|
786
|
-
const
|
|
787
|
-
if (
|
|
822
|
+
const existingByToken = this.cached.find((account) => account.refreshToken === auth.refresh);
|
|
823
|
+
if (existingByToken) return;
|
|
824
|
+
if (email) {
|
|
825
|
+
const existingByEmail = this.cached.find(
|
|
826
|
+
(account) => account.email && account.email === email
|
|
827
|
+
);
|
|
828
|
+
if (existingByEmail?.uuid) {
|
|
829
|
+
await this.replaceAccountCredentials(existingByEmail.uuid, auth);
|
|
830
|
+
return;
|
|
831
|
+
}
|
|
832
|
+
}
|
|
788
833
|
const newAccount = this.createNewAccount(auth, Date.now());
|
|
834
|
+
if (email) newAccount.email = email;
|
|
789
835
|
await this.store.addAccount(newAccount);
|
|
790
836
|
this.activeAccountUuid = newAccount.uuid;
|
|
791
837
|
await this.store.setActiveUuid(newAccount.uuid);
|
|
@@ -1111,7 +1157,9 @@ var MAX_SERVER_RETRIES_PER_ATTEMPT = 2;
|
|
|
1111
1157
|
var MAX_RESOLVE_ATTEMPTS = 10;
|
|
1112
1158
|
var SERVER_RETRY_BASE_MS = 1e3;
|
|
1113
1159
|
var SERVER_RETRY_MAX_MS = 4e3;
|
|
1114
|
-
|
|
1160
|
+
function isAbortError(error) {
|
|
1161
|
+
return error instanceof Error && error.name === "AbortError";
|
|
1162
|
+
}
|
|
1115
1163
|
function createExecutorForProvider(providerName, dependencies) {
|
|
1116
1164
|
const {
|
|
1117
1165
|
handleRateLimitResponse: handleRateLimitResponse2,
|
|
@@ -1122,77 +1170,49 @@ function createExecutorForProvider(providerName, dependencies) {
|
|
|
1122
1170
|
} = dependencies;
|
|
1123
1171
|
async function executeWithAccountRotation2(manager, runtimeFactory, client, input, init) {
|
|
1124
1172
|
const maxRetries = Math.max(MIN_MAX_RETRIES, manager.getAccountCount() * RETRIES_PER_ACCOUNT);
|
|
1125
|
-
let retries = 0;
|
|
1126
1173
|
let previousAccountUuid;
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
);
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
let response;
|
|
1143
|
-
try {
|
|
1144
|
-
runtime = await runtimeFactory.getRuntime(accountUuid);
|
|
1145
|
-
response = await runtime.fetch(input, init);
|
|
1146
|
-
} catch (error) {
|
|
1147
|
-
if (await handleRuntimeFetchFailure(manager, runtimeFactory, client, account, error)) {
|
|
1148
|
-
continue;
|
|
1174
|
+
async function retryServerErrors(account, runtime) {
|
|
1175
|
+
for (let attempt = 0; attempt < MAX_SERVER_RETRIES_PER_ATTEMPT; attempt++) {
|
|
1176
|
+
const backoff = Math.min(SERVER_RETRY_BASE_MS * 2 ** attempt, SERVER_RETRY_MAX_MS);
|
|
1177
|
+
const jitteredBackoff = backoff * (0.5 + Math.random() * 0.5);
|
|
1178
|
+
await sleep2(jitteredBackoff);
|
|
1179
|
+
let retryResponse;
|
|
1180
|
+
try {
|
|
1181
|
+
retryResponse = await runtime.fetch(input, init);
|
|
1182
|
+
} catch (error) {
|
|
1183
|
+
if (isAbortError(error)) throw error;
|
|
1184
|
+
if (await handleRuntimeFetchFailure(manager, runtimeFactory, client, account, error)) {
|
|
1185
|
+
return null;
|
|
1186
|
+
}
|
|
1187
|
+
void showToast2(client, `${getAccountLabel2(account)} network error \u2014 switching`, "warning");
|
|
1188
|
+
return null;
|
|
1149
1189
|
}
|
|
1150
|
-
|
|
1151
|
-
continue;
|
|
1190
|
+
if (retryResponse.status < 500) return retryResponse;
|
|
1152
1191
|
}
|
|
1192
|
+
return null;
|
|
1193
|
+
}
|
|
1194
|
+
const dispatchResponseStatus = async (account, accountUuid, runtime, response, allow401Retry, from401RefreshRetry) => {
|
|
1153
1195
|
if (response.status >= 500) {
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1196
|
+
const recovered = await retryServerErrors(account, runtime);
|
|
1197
|
+
if (recovered === null) {
|
|
1198
|
+
return { type: "retryOuter" };
|
|
1199
|
+
}
|
|
1200
|
+
response = recovered;
|
|
1201
|
+
}
|
|
1202
|
+
if (response.status === 401) {
|
|
1203
|
+
if (allow401Retry) {
|
|
1204
|
+
runtimeFactory.invalidate(accountUuid);
|
|
1161
1205
|
try {
|
|
1162
|
-
|
|
1206
|
+
const retryRuntime = await runtimeFactory.getRuntime(accountUuid);
|
|
1207
|
+
const retryResponse = await retryRuntime.fetch(input, init);
|
|
1208
|
+
return dispatchResponseStatus(account, accountUuid, retryRuntime, retryResponse, false, true);
|
|
1163
1209
|
} catch (error) {
|
|
1210
|
+
if (isAbortError(error)) throw error;
|
|
1164
1211
|
if (await handleRuntimeFetchFailure(manager, runtimeFactory, client, account, error)) {
|
|
1165
|
-
|
|
1166
|
-
break;
|
|
1212
|
+
return { type: "retryOuter" };
|
|
1167
1213
|
}
|
|
1168
|
-
|
|
1169
|
-
void showToast2(client, `${getAccountLabel2(account)} network error \u2014 switching`, "warning");
|
|
1170
|
-
break;
|
|
1214
|
+
return { type: "retryOuter" };
|
|
1171
1215
|
}
|
|
1172
|
-
if (serverResponse.status < 500) break;
|
|
1173
|
-
}
|
|
1174
|
-
if (authFailureDuringServerRetry) {
|
|
1175
|
-
continue;
|
|
1176
|
-
}
|
|
1177
|
-
if (networkErrorDuringServerRetry || serverResponse.status >= 500) {
|
|
1178
|
-
continue;
|
|
1179
|
-
}
|
|
1180
|
-
response = serverResponse;
|
|
1181
|
-
}
|
|
1182
|
-
if (response.status === 401) {
|
|
1183
|
-
runtimeFactory.invalidate(accountUuid);
|
|
1184
|
-
try {
|
|
1185
|
-
const retryRuntime = await runtimeFactory.getRuntime(accountUuid);
|
|
1186
|
-
const retryResponse = await retryRuntime.fetch(input, init);
|
|
1187
|
-
if (retryResponse.status !== 401) {
|
|
1188
|
-
await manager.markSuccess(accountUuid);
|
|
1189
|
-
return retryResponse;
|
|
1190
|
-
}
|
|
1191
|
-
} catch (error) {
|
|
1192
|
-
if (await handleRuntimeFetchFailure(manager, runtimeFactory, client, account, error)) {
|
|
1193
|
-
continue;
|
|
1194
|
-
}
|
|
1195
|
-
continue;
|
|
1196
1216
|
}
|
|
1197
1217
|
await manager.markAuthFailure(accountUuid, { ok: false, permanent: false });
|
|
1198
1218
|
await manager.refresh();
|
|
@@ -1203,7 +1223,7 @@ function createExecutorForProvider(providerName, dependencies) {
|
|
|
1203
1223
|
);
|
|
1204
1224
|
}
|
|
1205
1225
|
void showToast2(client, `${getAccountLabel2(account)} auth failed \u2014 switching to next account.`, "warning");
|
|
1206
|
-
|
|
1226
|
+
return { type: "retryOuter" };
|
|
1207
1227
|
}
|
|
1208
1228
|
if (response.status === 403) {
|
|
1209
1229
|
const revoked = await isRevokedTokenResponse(response);
|
|
@@ -1220,26 +1240,62 @@ function createExecutorForProvider(providerName, dependencies) {
|
|
|
1220
1240
|
`All ${providerName} accounts have been revoked or disabled. Re-authenticate with \`opencode auth login\`.`
|
|
1221
1241
|
);
|
|
1222
1242
|
}
|
|
1223
|
-
|
|
1243
|
+
return { type: "retryOuter" };
|
|
1244
|
+
}
|
|
1245
|
+
if (from401RefreshRetry) {
|
|
1246
|
+
return { type: "handled", response };
|
|
1224
1247
|
}
|
|
1225
1248
|
}
|
|
1226
1249
|
if (response.status === 429) {
|
|
1227
1250
|
await handleRateLimitResponse2(manager, client, account, response);
|
|
1251
|
+
return { type: "handled" };
|
|
1252
|
+
}
|
|
1253
|
+
return { type: "success", response };
|
|
1254
|
+
};
|
|
1255
|
+
for (let retries = 1; retries <= maxRetries; retries++) {
|
|
1256
|
+
await manager.refresh();
|
|
1257
|
+
const account = await resolveAccount(manager, client);
|
|
1258
|
+
const accountUuid = account.uuid;
|
|
1259
|
+
if (!accountUuid) continue;
|
|
1260
|
+
if (previousAccountUuid && accountUuid !== previousAccountUuid && manager.getAccountCount() > 1) {
|
|
1261
|
+
void showToast2(client, `Switched to ${getAccountLabel2(account)}`, "info");
|
|
1262
|
+
}
|
|
1263
|
+
previousAccountUuid = accountUuid;
|
|
1264
|
+
let runtime;
|
|
1265
|
+
let response;
|
|
1266
|
+
try {
|
|
1267
|
+
runtime = await runtimeFactory.getRuntime(accountUuid);
|
|
1268
|
+
response = await runtime.fetch(input, init);
|
|
1269
|
+
} catch (error) {
|
|
1270
|
+
if (isAbortError(error)) throw error;
|
|
1271
|
+
if (await handleRuntimeFetchFailure(manager, runtimeFactory, client, account, error)) {
|
|
1272
|
+
continue;
|
|
1273
|
+
}
|
|
1274
|
+
void showToast2(client, `${getAccountLabel2(account)} network error \u2014 switching`, "warning");
|
|
1275
|
+
continue;
|
|
1276
|
+
}
|
|
1277
|
+
const transition = await dispatchResponseStatus(account, accountUuid, runtime, response, true, false);
|
|
1278
|
+
if (transition.type === "retryOuter" || transition.type === "handled") {
|
|
1279
|
+
if (transition.type === "handled" && transition.response) {
|
|
1280
|
+
return transition.response;
|
|
1281
|
+
}
|
|
1228
1282
|
continue;
|
|
1229
1283
|
}
|
|
1230
1284
|
await manager.markSuccess(accountUuid);
|
|
1231
|
-
return response;
|
|
1285
|
+
return transition.response;
|
|
1232
1286
|
}
|
|
1287
|
+
throw new Error(
|
|
1288
|
+
`Exhausted ${maxRetries} retries across all accounts. All attempts failed due to auth errors, rate limits, or token issues.`
|
|
1289
|
+
);
|
|
1233
1290
|
}
|
|
1234
1291
|
async function handleRuntimeFetchFailure(manager, runtimeFactory, client, account, error) {
|
|
1235
|
-
|
|
1236
|
-
if (refreshFailureStatus === void 0) return false;
|
|
1292
|
+
if (!isTokenRefreshError(error)) return false;
|
|
1237
1293
|
if (!account.uuid) return false;
|
|
1238
1294
|
const accountUuid = account.uuid;
|
|
1239
1295
|
runtimeFactory.invalidate(accountUuid);
|
|
1240
1296
|
await manager.markAuthFailure(accountUuid, {
|
|
1241
1297
|
ok: false,
|
|
1242
|
-
permanent:
|
|
1298
|
+
permanent: error.permanent
|
|
1243
1299
|
});
|
|
1244
1300
|
await manager.refresh();
|
|
1245
1301
|
if (!manager.hasAnyUsableAccount()) {
|
|
@@ -1284,13 +1340,6 @@ function createExecutorForProvider(providerName, dependencies) {
|
|
|
1284
1340
|
executeWithAccountRotation: executeWithAccountRotation2
|
|
1285
1341
|
};
|
|
1286
1342
|
}
|
|
1287
|
-
function getRefreshFailureStatus(error) {
|
|
1288
|
-
if (!(error instanceof Error)) return void 0;
|
|
1289
|
-
const matched = error.message.match(/Token refresh failed:\s*(\d{3})/);
|
|
1290
|
-
if (!matched) return void 0;
|
|
1291
|
-
const status = Number(matched[1]);
|
|
1292
|
-
return Number.isFinite(status) ? status : void 0;
|
|
1293
|
-
}
|
|
1294
1343
|
async function isRevokedTokenResponse(response) {
|
|
1295
1344
|
try {
|
|
1296
1345
|
const cloned = response.clone();
|
|
@@ -1305,6 +1354,7 @@ async function isRevokedTokenResponse(response) {
|
|
|
1305
1354
|
var INITIAL_DELAY_MS = 5e3;
|
|
1306
1355
|
function createProactiveRefreshQueueForProvider(dependencies) {
|
|
1307
1356
|
const {
|
|
1357
|
+
providerAuthId,
|
|
1308
1358
|
getConfig: getConfig2,
|
|
1309
1359
|
refreshToken: refreshToken2,
|
|
1310
1360
|
isTokenExpired: isTokenExpired2,
|
|
@@ -1323,6 +1373,10 @@ function createProactiveRefreshQueueForProvider(dependencies) {
|
|
|
1323
1373
|
const config = getConfig2();
|
|
1324
1374
|
if (!config.proactive_refresh) return;
|
|
1325
1375
|
this.runToken++;
|
|
1376
|
+
if (this.timeoutHandle) {
|
|
1377
|
+
clearTimeout(this.timeoutHandle);
|
|
1378
|
+
this.timeoutHandle = null;
|
|
1379
|
+
}
|
|
1326
1380
|
this.scheduleNext(this.runToken, INITIAL_DELAY_MS);
|
|
1327
1381
|
debugLog2(this.client, "Proactive refresh started", {
|
|
1328
1382
|
intervalSeconds: config.proactive_refresh_interval_seconds,
|
|
@@ -1397,23 +1451,41 @@ function createProactiveRefreshQueueForProvider(dependencies) {
|
|
|
1397
1451
|
}
|
|
1398
1452
|
async persistFailure(account, permanent) {
|
|
1399
1453
|
try {
|
|
1400
|
-
|
|
1401
|
-
|
|
1454
|
+
const accountUuid = account.uuid;
|
|
1455
|
+
if (!accountUuid) return;
|
|
1456
|
+
if (permanent) {
|
|
1457
|
+
const removed = await this.store.removeAccount(accountUuid);
|
|
1458
|
+
if (!removed) return;
|
|
1459
|
+
this.onInvalidate?.(accountUuid);
|
|
1460
|
+
await this.clearOpenCodeAuthIfNoAccountsRemain();
|
|
1461
|
+
return;
|
|
1462
|
+
}
|
|
1463
|
+
await this.store.mutateStorage((storage) => {
|
|
1464
|
+
const target = storage.accounts.find((entry) => entry.uuid === accountUuid);
|
|
1465
|
+
if (!target) return;
|
|
1466
|
+
target.consecutiveAuthFailures = (target.consecutiveAuthFailures ?? 0) + 1;
|
|
1467
|
+
const maxFailures = getConfig2().max_consecutive_auth_failures;
|
|
1468
|
+
const usableCount = storage.accounts.filter(
|
|
1469
|
+
(entry) => entry.enabled && !entry.isAuthDisabled && entry.uuid !== accountUuid
|
|
1470
|
+
).length;
|
|
1471
|
+
if (target.consecutiveAuthFailures >= maxFailures && usableCount > 0) {
|
|
1402
1472
|
target.isAuthDisabled = true;
|
|
1403
|
-
target.authDisabledReason =
|
|
1404
|
-
} else {
|
|
1405
|
-
target.consecutiveAuthFailures = (target.consecutiveAuthFailures ?? 0) + 1;
|
|
1406
|
-
const maxFailures = getConfig2().max_consecutive_auth_failures;
|
|
1407
|
-
if (target.consecutiveAuthFailures >= maxFailures) {
|
|
1408
|
-
target.isAuthDisabled = true;
|
|
1409
|
-
target.authDisabledReason = `${maxFailures} consecutive auth failures (proactive refresh)`;
|
|
1410
|
-
}
|
|
1473
|
+
target.authDisabledReason = `${maxFailures} consecutive auth failures (proactive refresh)`;
|
|
1411
1474
|
}
|
|
1412
1475
|
});
|
|
1413
1476
|
} catch {
|
|
1414
1477
|
debugLog2(this.client, `Failed to persist auth failure for ${account.uuid}`);
|
|
1415
1478
|
}
|
|
1416
1479
|
}
|
|
1480
|
+
async clearOpenCodeAuthIfNoAccountsRemain() {
|
|
1481
|
+
const storage = await this.store.load();
|
|
1482
|
+
if (storage.accounts.length > 0) return;
|
|
1483
|
+
await this.client.auth.set({
|
|
1484
|
+
path: { id: providerAuthId },
|
|
1485
|
+
body: getClearedOAuthBody()
|
|
1486
|
+
}).catch(() => {
|
|
1487
|
+
});
|
|
1488
|
+
}
|
|
1417
1489
|
};
|
|
1418
1490
|
}
|
|
1419
1491
|
|
|
@@ -1763,6 +1835,8 @@ var openAIOAuthAdapter = {
|
|
|
1763
1835
|
oauthBetaHeader: "",
|
|
1764
1836
|
requestBetaHeader: "",
|
|
1765
1837
|
cliUserAgent: "opencode/1.1.53",
|
|
1838
|
+
cliVersion: "",
|
|
1839
|
+
billingSalt: "",
|
|
1766
1840
|
toolPrefix: "mcp_",
|
|
1767
1841
|
accountStorageFilename: "openai-multi-account-accounts.json",
|
|
1768
1842
|
transform: {
|
|
@@ -2353,11 +2427,11 @@ function getUsageSummary(account) {
|
|
|
2353
2427
|
const parts = [];
|
|
2354
2428
|
const { five_hour, seven_day } = parsed.output;
|
|
2355
2429
|
if (five_hour) {
|
|
2356
|
-
const reset = five_hour.
|
|
2430
|
+
const reset = five_hour.resets_at ? ` (resets ${formatTimeRemaining(five_hour.resets_at)})` : "";
|
|
2357
2431
|
parts.push(`5h: ${five_hour.utilization.toFixed(0)}%${reset}`);
|
|
2358
2432
|
}
|
|
2359
2433
|
if (seven_day) {
|
|
2360
|
-
const reset = seven_day.
|
|
2434
|
+
const reset = seven_day.resets_at ? ` (resets ${formatTimeRemaining(seven_day.resets_at)})` : "";
|
|
2361
2435
|
parts.push(`7d: ${seven_day.utilization.toFixed(0)}%${reset}`);
|
|
2362
2436
|
}
|
|
2363
2437
|
return parts.length > 0 ? parts.join(", ") : "no usage data";
|
|
@@ -2987,6 +3061,7 @@ Retrying authentication for ${label}...
|
|
|
2987
3061
|
|
|
2988
3062
|
// src/proactive-refresh.ts
|
|
2989
3063
|
var ProactiveRefreshQueue = createProactiveRefreshQueueForProvider({
|
|
3064
|
+
providerAuthId: "openai",
|
|
2990
3065
|
getConfig,
|
|
2991
3066
|
isTokenExpired,
|
|
2992
3067
|
refreshToken,
|
|
@@ -3097,10 +3172,7 @@ var AccountRuntimeFactory = class {
|
|
|
3097
3172
|
if (!accessToken || !expiresAt || isTokenExpired({ accessToken, expiresAt })) {
|
|
3098
3173
|
const refreshed = await refreshToken(storedAccount.refreshToken, uuid, this.client);
|
|
3099
3174
|
if (!refreshed.ok) {
|
|
3100
|
-
|
|
3101
|
-
throw new Error(`Token refresh failed: ${refreshed.status}`);
|
|
3102
|
-
}
|
|
3103
|
-
throw new Error("Token refresh failed");
|
|
3175
|
+
throw new TokenRefreshError(refreshed.permanent, refreshed.status);
|
|
3104
3176
|
}
|
|
3105
3177
|
accessToken = refreshed.patch.accessToken;
|
|
3106
3178
|
expiresAt = refreshed.patch.expiresAt;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-codex-multi-account",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.4",
|
|
4
4
|
"description": "OpenCode plugin for Codex (OpenAI) multi-account management with automatic rate limit switching",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
"directory": "packages/codex-multi-account"
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
|
-
"opencode-multi-account-core": "^0.2.
|
|
43
|
+
"opencode-multi-account-core": "^0.2.4",
|
|
44
44
|
"valibot": "^1.2.0"
|
|
45
45
|
},
|
|
46
46
|
"devDependencies": {
|