oc-chatgpt-multi-auth 4.11.2 → 4.12.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +56 -20
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +114 -42
- package/dist/index.js.map +1 -1
- package/dist/lib/audit.d.ts +45 -0
- package/dist/lib/audit.d.ts.map +1 -0
- package/dist/lib/audit.js +131 -0
- package/dist/lib/audit.js.map +1 -0
- package/dist/lib/auth/auth.d.ts.map +1 -1
- package/dist/lib/auth/auth.js +12 -11
- package/dist/lib/auth/auth.js.map +1 -1
- package/dist/lib/auth-rate-limit.d.ts +20 -0
- package/dist/lib/auth-rate-limit.d.ts.map +1 -0
- package/dist/lib/auth-rate-limit.js +91 -0
- package/dist/lib/auth-rate-limit.js.map +1 -0
- package/dist/lib/circuit-breaker.d.ts +34 -0
- package/dist/lib/circuit-breaker.d.ts.map +1 -0
- package/dist/lib/circuit-breaker.js +117 -0
- package/dist/lib/circuit-breaker.js.map +1 -0
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/config.js +5 -1
- package/dist/lib/config.js.map +1 -1
- package/dist/lib/logger.d.ts +5 -1
- package/dist/lib/logger.d.ts.map +1 -1
- package/dist/lib/logger.js +41 -4
- package/dist/lib/logger.js.map +1 -1
- package/dist/lib/proactive-refresh.d.ts +66 -0
- package/dist/lib/proactive-refresh.d.ts.map +1 -0
- package/dist/lib/proactive-refresh.js +143 -0
- package/dist/lib/proactive-refresh.js.map +1 -0
- package/dist/lib/request/response-handler.d.ts.map +1 -1
- package/dist/lib/request/response-handler.js +4 -0
- package/dist/lib/request/response-handler.js.map +1 -1
- package/dist/lib/rotation.d.ts.map +1 -1
- package/dist/lib/rotation.js +13 -0
- package/dist/lib/rotation.js.map +1 -1
- package/dist/lib/schemas.d.ts +363 -0
- package/dist/lib/schemas.d.ts.map +1 -0
- package/dist/lib/schemas.js +229 -0
- package/dist/lib/schemas.js.map +1 -0
- package/dist/lib/storage.d.ts +34 -1
- package/dist/lib/storage.d.ts.map +1 -1
- package/dist/lib/storage.js +182 -5
- package/dist/lib/storage.js.map +1 -1
- package/dist/lib/types.d.ts +1 -73
- package/dist/lib/types.d.ts.map +1 -1
- package/package.json +6 -3
package/README.md
CHANGED
|
@@ -254,12 +254,14 @@ opencode auth login # Run again to add more accounts
|
|
|
254
254
|
|
|
255
255
|
The plugin provides built-in tools for managing your OpenAI accounts. These are available directly in OpenCode — just ask the agent or type the tool name.
|
|
256
256
|
|
|
257
|
-
|
|
257
|
+
> **Note:** Tools were renamed from `openai-accounts-*` to `codex-*` in v4.12.0 for brevity.
|
|
258
|
+
|
|
259
|
+
### codex-list
|
|
258
260
|
|
|
259
261
|
List all configured accounts with their status.
|
|
260
262
|
|
|
261
263
|
```
|
|
262
|
-
|
|
264
|
+
codex-list
|
|
263
265
|
```
|
|
264
266
|
|
|
265
267
|
**Output:**
|
|
@@ -270,17 +272,17 @@ OpenAI Accounts (3 total):
|
|
|
270
272
|
[2] work@company.com
|
|
271
273
|
[3] backup@email.com
|
|
272
274
|
|
|
273
|
-
Use
|
|
275
|
+
Use codex-switch to change active account.
|
|
274
276
|
```
|
|
275
277
|
|
|
276
278
|
---
|
|
277
279
|
|
|
278
|
-
###
|
|
280
|
+
### codex-switch
|
|
279
281
|
|
|
280
282
|
Switch to a different account by index (1-based).
|
|
281
283
|
|
|
282
284
|
```
|
|
283
|
-
|
|
285
|
+
codex-switch index=2
|
|
284
286
|
```
|
|
285
287
|
|
|
286
288
|
**Output:**
|
|
@@ -290,12 +292,12 @@ Switched to account [2] work@company.com
|
|
|
290
292
|
|
|
291
293
|
---
|
|
292
294
|
|
|
293
|
-
###
|
|
295
|
+
### codex-status
|
|
294
296
|
|
|
295
297
|
Show detailed status including rate limits and health scores.
|
|
296
298
|
|
|
297
299
|
```
|
|
298
|
-
|
|
300
|
+
codex-status
|
|
299
301
|
```
|
|
300
302
|
|
|
301
303
|
**Output:**
|
|
@@ -317,12 +319,12 @@ OpenAI Account Status:
|
|
|
317
319
|
|
|
318
320
|
---
|
|
319
321
|
|
|
320
|
-
###
|
|
322
|
+
### codex-health
|
|
321
323
|
|
|
322
324
|
Check if all account tokens are still valid (read-only check).
|
|
323
325
|
|
|
324
326
|
```
|
|
325
|
-
|
|
327
|
+
codex-health
|
|
326
328
|
```
|
|
327
329
|
|
|
328
330
|
**Output:**
|
|
@@ -338,12 +340,12 @@ Summary: 2 healthy, 1 unhealthy
|
|
|
338
340
|
|
|
339
341
|
---
|
|
340
342
|
|
|
341
|
-
###
|
|
343
|
+
### codex-refresh
|
|
342
344
|
|
|
343
345
|
Refresh all OAuth tokens and save them to disk. Use this after long idle periods.
|
|
344
346
|
|
|
345
347
|
```
|
|
346
|
-
|
|
348
|
+
codex-refresh
|
|
347
349
|
```
|
|
348
350
|
|
|
349
351
|
**Output:**
|
|
@@ -357,16 +359,16 @@ Refreshing 3 account(s):
|
|
|
357
359
|
Summary: 2 refreshed, 1 failed
|
|
358
360
|
```
|
|
359
361
|
|
|
360
|
-
**Difference from health check:** `
|
|
362
|
+
**Difference from health check:** `codex-health` only validates tokens. `codex-refresh` actually refreshes them and saves new tokens to disk.
|
|
361
363
|
|
|
362
364
|
---
|
|
363
365
|
|
|
364
|
-
###
|
|
366
|
+
### codex-remove
|
|
365
367
|
|
|
366
368
|
Remove an account by index. Useful for cleaning up expired accounts.
|
|
367
369
|
|
|
368
370
|
```
|
|
369
|
-
|
|
371
|
+
codex-remove index=3
|
|
370
372
|
```
|
|
371
373
|
|
|
372
374
|
**Output:**
|
|
@@ -378,16 +380,50 @@ Remaining accounts: 2
|
|
|
378
380
|
|
|
379
381
|
---
|
|
380
382
|
|
|
383
|
+
### codex-export
|
|
384
|
+
|
|
385
|
+
Export all accounts to a portable JSON file. Useful for backup or migration.
|
|
386
|
+
|
|
387
|
+
```
|
|
388
|
+
codex-export path="~/backup/accounts.json"
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
**Output:**
|
|
392
|
+
```
|
|
393
|
+
Exported 3 account(s) to ~/backup/accounts.json
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
---
|
|
397
|
+
|
|
398
|
+
### codex-import
|
|
399
|
+
|
|
400
|
+
Import accounts from a JSON file (exported via `codex-export`). Merges with existing accounts.
|
|
401
|
+
|
|
402
|
+
```
|
|
403
|
+
codex-import path="~/backup/accounts.json"
|
|
404
|
+
```
|
|
405
|
+
|
|
406
|
+
**Output:**
|
|
407
|
+
```
|
|
408
|
+
Imported 2 new account(s) (1 duplicate skipped)
|
|
409
|
+
|
|
410
|
+
Total accounts: 4
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
---
|
|
414
|
+
|
|
381
415
|
### Quick Reference
|
|
382
416
|
|
|
383
417
|
| Tool | What It Does | Example |
|
|
384
418
|
|------|--------------|---------|
|
|
385
|
-
| `
|
|
386
|
-
| `
|
|
387
|
-
| `
|
|
388
|
-
| `
|
|
389
|
-
| `
|
|
390
|
-
| `
|
|
419
|
+
| `codex-list` | List all accounts | "list my accounts" |
|
|
420
|
+
| `codex-switch` | Switch active account | "switch to account 2" |
|
|
421
|
+
| `codex-status` | Show rate limits & health | "show account status" |
|
|
422
|
+
| `codex-health` | Validate tokens (read-only) | "check account health" |
|
|
423
|
+
| `codex-refresh` | Refresh & save tokens | "refresh my tokens" |
|
|
424
|
+
| `codex-remove` | Remove an account | "remove account 3" |
|
|
425
|
+
| `codex-export` | Export accounts to file | "export my accounts" |
|
|
426
|
+
| `codex-import` | Import accounts from file | "import accounts from backup" |
|
|
391
427
|
|
|
392
428
|
---
|
|
393
429
|
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAGH,OAAO,KAAK,EAAE,MAAM,EAAe,MAAM,qBAAqB,CAAC;AA0E/D;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,iBAAiB,EAAE,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAGH,OAAO,KAAK,EAAE,MAAM,EAAe,MAAM,qBAAqB,CAAC;AA0E/D;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,iBAAiB,EAAE,MAi/C/B,CAAC;AAEF,eAAO,MAAM,gBAAgB,QAAoB,CAAC;AAElD,eAAe,iBAAiB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -30,11 +30,11 @@ import { startLocalOAuthServer } from "./lib/auth/server.js";
|
|
|
30
30
|
import { promptAccountSelection, promptLoginMode } from "./lib/cli.js";
|
|
31
31
|
import { getCodexMode, getRateLimitToastDebounceMs, getRetryAllAccountsMaxRetries, getRetryAllAccountsMaxWaitMs, getRetryAllAccountsRateLimited, getTokenRefreshSkewMs, getSessionRecovery, getAutoResume, getToastDurationMs, getPerProjectAccounts, loadPluginConfig, } from "./lib/config.js";
|
|
32
32
|
import { AUTH_LABELS, CODEX_BASE_URL, DUMMY_API_KEY, LOG_STAGES, PLUGIN_NAME, PROVIDER_ID, ACCOUNT_LIMITS, } from "./lib/constants.js";
|
|
33
|
-
import { initLogger, logRequest, logDebug, logInfo, logWarn } from "./lib/logger.js";
|
|
33
|
+
import { initLogger, logRequest, logDebug, logInfo, logWarn, logError } from "./lib/logger.js";
|
|
34
34
|
import { checkAndNotify } from "./lib/auto-update-checker.js";
|
|
35
35
|
import { handleContextOverflow } from "./lib/context-overflow.js";
|
|
36
36
|
import { AccountManager, getAccountIdCandidates, extractAccountEmail, extractAccountId, formatAccountLabel, formatCooldown, formatWaitTime, sanitizeEmail, shouldUpdateAccountIdFromToken, } from "./lib/accounts.js";
|
|
37
|
-
import { getStoragePath, loadAccounts, saveAccounts, setStoragePath } from "./lib/storage.js";
|
|
37
|
+
import { getStoragePath, loadAccounts, saveAccounts, setStoragePath, exportAccounts, importAccounts, StorageError, formatStorageErrorHint } from "./lib/storage.js";
|
|
38
38
|
import { createCodexHeaders, extractRequestUrl, handleErrorResponse, handleSuccessResponse, refreshAndUpdateToken, rewriteUrlForCodex, shouldRefreshToken, transformRequestForCodex, } from "./lib/request/fetch-helpers.js";
|
|
39
39
|
import { getRateLimitBackoff, RATE_LIMIT_SHORT_RETRY_THRESHOLD_MS, resetRateLimitBackoff, } from "./lib/request/rate-limit-backoff.js";
|
|
40
40
|
import { addJitter } from "./lib/rotation.js";
|
|
@@ -573,7 +573,8 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
573
573
|
continue;
|
|
574
574
|
}
|
|
575
575
|
const hadAccountId = !!account.accountId;
|
|
576
|
-
|
|
576
|
+
// Prefer fresh token-derived ID over stored ID (fixes Business plan workspace issues)
|
|
577
|
+
const accountId = extractAccountId(accountAuth.access) ?? account.accountId;
|
|
577
578
|
if (!accountId) {
|
|
578
579
|
accountManager.markAccountCoolingDown(account, ACCOUNT_LIMITS.AUTH_FAILURE_COOLDOWN_MS, "auth-failure");
|
|
579
580
|
accountManager.saveToDiskDebounced();
|
|
@@ -596,14 +597,26 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
596
597
|
promptCacheKey,
|
|
597
598
|
});
|
|
598
599
|
while (true) {
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
600
|
+
let response;
|
|
601
|
+
const fetchStart = performance.now();
|
|
602
|
+
try {
|
|
603
|
+
response = await fetch(url, {
|
|
604
|
+
...requestInit,
|
|
605
|
+
headers,
|
|
606
|
+
});
|
|
607
|
+
}
|
|
608
|
+
catch (networkError) {
|
|
609
|
+
const errorMsg = networkError instanceof Error ? networkError.message : String(networkError);
|
|
610
|
+
logWarn(`Network error for account ${account.index + 1}: ${errorMsg}`);
|
|
611
|
+
accountManager.recordFailure(account, modelFamily, model);
|
|
612
|
+
break;
|
|
613
|
+
}
|
|
614
|
+
const fetchLatencyMs = Math.round(performance.now() - fetchStart);
|
|
603
615
|
logRequest(LOG_STAGES.RESPONSE, {
|
|
604
616
|
status: response.status,
|
|
605
617
|
ok: response.ok,
|
|
606
618
|
statusText: response.statusText,
|
|
619
|
+
latencyMs: fetchLatencyMs,
|
|
607
620
|
headers: Object.fromEntries(response.headers.entries()),
|
|
608
621
|
});
|
|
609
622
|
if (!response.ok) {
|
|
@@ -663,7 +676,7 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
663
676
|
}
|
|
664
677
|
const waitLabel = waitMs > 0 ? formatWaitTime(waitMs) : "a bit";
|
|
665
678
|
const message = count === 0
|
|
666
|
-
? "No
|
|
679
|
+
? "No Codex accounts configured. Run `opencode auth login`."
|
|
667
680
|
: `All ${count} account(s) are rate-limited. Try again in ${waitLabel} or add another account with \`opencode auth login\`.`;
|
|
668
681
|
return new Response(JSON.stringify({ error: { message } }), {
|
|
669
682
|
status: 429,
|
|
@@ -783,9 +796,10 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
783
796
|
}
|
|
784
797
|
catch (err) {
|
|
785
798
|
const storagePath = getStoragePath();
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
799
|
+
const errorCode = err?.code || "UNKNOWN";
|
|
800
|
+
const hint = err instanceof StorageError ? err.hint : formatStorageErrorHint(err, storagePath);
|
|
801
|
+
logError(`[${PLUGIN_NAME}] Failed to persist account: [${errorCode}] ${err?.message ?? String(err)}`);
|
|
802
|
+
await showToast(hint, "error", { title: "Account Persistence Failed", duration: 10000 });
|
|
789
803
|
}
|
|
790
804
|
});
|
|
791
805
|
}
|
|
@@ -832,9 +846,10 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
832
846
|
}
|
|
833
847
|
catch (err) {
|
|
834
848
|
const storagePath = getStoragePath();
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
849
|
+
const errorCode = err?.code || "UNKNOWN";
|
|
850
|
+
const hint = err instanceof StorageError ? err.hint : formatStorageErrorHint(err, storagePath);
|
|
851
|
+
logError(`[${PLUGIN_NAME}] Failed to persist account: [${errorCode}] ${err?.message ?? String(err)}`);
|
|
852
|
+
await showToast(hint, "error", { title: "Account Persistence Failed", duration: 10000 });
|
|
838
853
|
}
|
|
839
854
|
if (accounts.length >= ACCOUNT_LIMITS.MAX_ACCOUNTS) {
|
|
840
855
|
break;
|
|
@@ -880,9 +895,10 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
880
895
|
}
|
|
881
896
|
catch (err) {
|
|
882
897
|
const storagePath = getStoragePath();
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
898
|
+
const errorCode = err?.code || "UNKNOWN";
|
|
899
|
+
const hint = err instanceof StorageError ? err.hint : formatStorageErrorHint(err, storagePath);
|
|
900
|
+
logError(`[${PLUGIN_NAME}] Failed to persist account: [${errorCode}] ${err?.message ?? String(err)}`);
|
|
901
|
+
await showToast(hint, "error", { title: "Account Persistence Failed", duration: 10000 });
|
|
886
902
|
}
|
|
887
903
|
});
|
|
888
904
|
},
|
|
@@ -890,15 +906,15 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
890
906
|
],
|
|
891
907
|
},
|
|
892
908
|
tool: {
|
|
893
|
-
"
|
|
894
|
-
description: "List all
|
|
909
|
+
"codex-list": tool({
|
|
910
|
+
description: "List all Codex OAuth accounts and the current active index.",
|
|
895
911
|
args: {},
|
|
896
912
|
async execute() {
|
|
897
913
|
const storage = await loadAccounts();
|
|
898
914
|
const storePath = getStoragePath();
|
|
899
915
|
if (!storage || storage.accounts.length === 0) {
|
|
900
916
|
return [
|
|
901
|
-
"No
|
|
917
|
+
"No Codex accounts configured.",
|
|
902
918
|
"",
|
|
903
919
|
"Add accounts:",
|
|
904
920
|
" opencode auth login",
|
|
@@ -909,7 +925,7 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
909
925
|
const now = Date.now();
|
|
910
926
|
const activeIndex = resolveActiveIndex(storage, "codex");
|
|
911
927
|
const lines = [
|
|
912
|
-
`
|
|
928
|
+
`Codex Accounts (${storage.accounts.length}):`,
|
|
913
929
|
"",
|
|
914
930
|
" # Label Status",
|
|
915
931
|
"----------------------------------------------- ---------------------",
|
|
@@ -936,20 +952,20 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
936
952
|
lines.push("");
|
|
937
953
|
lines.push("Commands:");
|
|
938
954
|
lines.push(" - Add account: opencode auth login");
|
|
939
|
-
lines.push(" - Switch account:
|
|
940
|
-
lines.push(" - Status details:
|
|
955
|
+
lines.push(" - Switch account: codex-switch");
|
|
956
|
+
lines.push(" - Status details: codex-status");
|
|
941
957
|
return lines.join("\n");
|
|
942
958
|
},
|
|
943
959
|
}),
|
|
944
|
-
"
|
|
945
|
-
description: "Switch active
|
|
960
|
+
"codex-switch": tool({
|
|
961
|
+
description: "Switch active Codex account by index (1-based).",
|
|
946
962
|
args: {
|
|
947
963
|
index: tool.schema.number().describe("Account number to switch to (1-based, e.g., 1 for first account)"),
|
|
948
964
|
},
|
|
949
965
|
async execute({ index }) {
|
|
950
966
|
const storage = await loadAccounts();
|
|
951
967
|
if (!storage || storage.accounts.length === 0) {
|
|
952
|
-
return "No
|
|
968
|
+
return "No Codex accounts configured. Run: opencode auth login";
|
|
953
969
|
}
|
|
954
970
|
const targetIndex = Math.floor((index ?? 0) - 1);
|
|
955
971
|
if (!Number.isFinite(targetIndex) ||
|
|
@@ -968,7 +984,13 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
968
984
|
for (const family of MODEL_FAMILIES) {
|
|
969
985
|
storage.activeIndexByFamily[family] = targetIndex;
|
|
970
986
|
}
|
|
971
|
-
|
|
987
|
+
try {
|
|
988
|
+
await saveAccounts(storage);
|
|
989
|
+
}
|
|
990
|
+
catch (saveError) {
|
|
991
|
+
logWarn("Failed to save account switch", { error: String(saveError) });
|
|
992
|
+
return `Switched to ${formatAccountLabel(account, targetIndex)} but failed to persist. Changes may be lost on restart.`;
|
|
993
|
+
}
|
|
972
994
|
if (cachedAccountManager) {
|
|
973
995
|
cachedAccountManager.setActiveIndex(targetIndex);
|
|
974
996
|
await cachedAccountManager.saveToDisk();
|
|
@@ -977,13 +999,13 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
977
999
|
return `Switched to account: ${label}`;
|
|
978
1000
|
},
|
|
979
1001
|
}),
|
|
980
|
-
"
|
|
981
|
-
description: "Show detailed status of
|
|
1002
|
+
"codex-status": tool({
|
|
1003
|
+
description: "Show detailed status of Codex accounts and rate limits.",
|
|
982
1004
|
args: {},
|
|
983
1005
|
async execute() {
|
|
984
1006
|
const storage = await loadAccounts();
|
|
985
1007
|
if (!storage || storage.accounts.length === 0) {
|
|
986
|
-
return "No
|
|
1008
|
+
return "No Codex accounts configured. Run: opencode auth login";
|
|
987
1009
|
}
|
|
988
1010
|
const now = Date.now();
|
|
989
1011
|
const activeIndex = resolveActiveIndex(storage, "codex");
|
|
@@ -1025,13 +1047,13 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
1025
1047
|
return lines.join("\n");
|
|
1026
1048
|
},
|
|
1027
1049
|
}),
|
|
1028
|
-
"
|
|
1029
|
-
description: "Check health of all
|
|
1050
|
+
"codex-health": tool({
|
|
1051
|
+
description: "Check health of all Codex accounts by validating refresh tokens.",
|
|
1030
1052
|
args: {},
|
|
1031
1053
|
async execute() {
|
|
1032
1054
|
const storage = await loadAccounts();
|
|
1033
1055
|
if (!storage || storage.accounts.length === 0) {
|
|
1034
|
-
return "No
|
|
1056
|
+
return "No Codex accounts configured. Run: opencode auth login";
|
|
1035
1057
|
}
|
|
1036
1058
|
const results = [
|
|
1037
1059
|
`Health Check (${storage.accounts.length} accounts):`,
|
|
@@ -1057,7 +1079,7 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
1057
1079
|
}
|
|
1058
1080
|
catch (error) {
|
|
1059
1081
|
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
1060
|
-
results.push(` ✗ ${label}: Error - ${errorMsg.slice(0,
|
|
1082
|
+
results.push(` ✗ ${label}: Error - ${errorMsg.slice(0, 120)}`);
|
|
1061
1083
|
unhealthyCount++;
|
|
1062
1084
|
}
|
|
1063
1085
|
}
|
|
@@ -1066,21 +1088,21 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
1066
1088
|
return results.join("\n");
|
|
1067
1089
|
},
|
|
1068
1090
|
}),
|
|
1069
|
-
"
|
|
1070
|
-
description: "Remove
|
|
1091
|
+
"codex-remove": tool({
|
|
1092
|
+
description: "Remove a Codex account by index (1-based). Use codex-list to list accounts first.",
|
|
1071
1093
|
args: {
|
|
1072
1094
|
index: tool.schema.number().describe("Account number to remove (1-based, e.g., 1 for first account)"),
|
|
1073
1095
|
},
|
|
1074
1096
|
async execute({ index }) {
|
|
1075
1097
|
const storage = await loadAccounts();
|
|
1076
1098
|
if (!storage || storage.accounts.length === 0) {
|
|
1077
|
-
return "No
|
|
1099
|
+
return "No Codex accounts configured. Nothing to remove.";
|
|
1078
1100
|
}
|
|
1079
1101
|
const targetIndex = Math.floor((index ?? 0) - 1);
|
|
1080
1102
|
if (!Number.isFinite(targetIndex) ||
|
|
1081
1103
|
targetIndex < 0 ||
|
|
1082
1104
|
targetIndex >= storage.accounts.length) {
|
|
1083
|
-
return `Invalid account number: ${index}\n\nValid range: 1-${storage.accounts.length}\n\nUse
|
|
1105
|
+
return `Invalid account number: ${index}\n\nValid range: 1-${storage.accounts.length}\n\nUse codex-list to list all accounts.`;
|
|
1084
1106
|
}
|
|
1085
1107
|
const account = storage.accounts[targetIndex];
|
|
1086
1108
|
if (!account) {
|
|
@@ -1113,7 +1135,13 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
1113
1135
|
}
|
|
1114
1136
|
}
|
|
1115
1137
|
}
|
|
1116
|
-
|
|
1138
|
+
try {
|
|
1139
|
+
await saveAccounts(storage);
|
|
1140
|
+
}
|
|
1141
|
+
catch (saveError) {
|
|
1142
|
+
logWarn("Failed to save account removal", { error: String(saveError) });
|
|
1143
|
+
return `Removed ${formatAccountLabel(account, targetIndex)} from memory but failed to persist. Changes may be lost on restart.`;
|
|
1144
|
+
}
|
|
1117
1145
|
if (cachedAccountManager) {
|
|
1118
1146
|
const managedAccounts = cachedAccountManager.getAccountsSnapshot();
|
|
1119
1147
|
const managedAccount = managedAccounts.find((acc) => acc.refreshToken === account.refreshToken);
|
|
@@ -1132,13 +1160,13 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
1132
1160
|
].join("\n");
|
|
1133
1161
|
},
|
|
1134
1162
|
}),
|
|
1135
|
-
"
|
|
1163
|
+
"codex-refresh": tool({
|
|
1136
1164
|
description: "Manually refresh OAuth tokens for all accounts to verify they're still valid.",
|
|
1137
1165
|
args: {},
|
|
1138
1166
|
async execute() {
|
|
1139
1167
|
const storage = await loadAccounts();
|
|
1140
1168
|
if (!storage || storage.accounts.length === 0) {
|
|
1141
|
-
return "No
|
|
1169
|
+
return "No Codex accounts configured. Run: opencode auth login";
|
|
1142
1170
|
}
|
|
1143
1171
|
const results = [
|
|
1144
1172
|
`Refreshing ${storage.accounts.length} account(s):`,
|
|
@@ -1165,7 +1193,7 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
1165
1193
|
}
|
|
1166
1194
|
catch (error) {
|
|
1167
1195
|
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
1168
|
-
results.push(` ✗ ${label}: Error - ${errorMsg.slice(0,
|
|
1196
|
+
results.push(` ✗ ${label}: Error - ${errorMsg.slice(0, 120)}`);
|
|
1169
1197
|
failedCount++;
|
|
1170
1198
|
}
|
|
1171
1199
|
}
|
|
@@ -1175,6 +1203,50 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
1175
1203
|
return results.join("\n");
|
|
1176
1204
|
},
|
|
1177
1205
|
}),
|
|
1206
|
+
"codex-export": tool({
|
|
1207
|
+
description: "Export accounts to a JSON file for backup or migration to another machine.",
|
|
1208
|
+
args: {
|
|
1209
|
+
path: tool.schema.string().describe("File path to export to (e.g., ~/codex-backup.json)"),
|
|
1210
|
+
force: tool.schema.boolean().optional().describe("Overwrite existing file (default: true)"),
|
|
1211
|
+
},
|
|
1212
|
+
async execute({ path: filePath, force }) {
|
|
1213
|
+
try {
|
|
1214
|
+
await exportAccounts(filePath, force ?? true);
|
|
1215
|
+
const storage = await loadAccounts();
|
|
1216
|
+
const count = storage?.accounts.length ?? 0;
|
|
1217
|
+
return `Exported ${count} account(s) to: ${filePath}`;
|
|
1218
|
+
}
|
|
1219
|
+
catch (error) {
|
|
1220
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
1221
|
+
return `Export failed: ${msg}`;
|
|
1222
|
+
}
|
|
1223
|
+
},
|
|
1224
|
+
}),
|
|
1225
|
+
"codex-import": tool({
|
|
1226
|
+
description: "Import accounts from a JSON file, merging with existing accounts.",
|
|
1227
|
+
args: {
|
|
1228
|
+
path: tool.schema.string().describe("File path to import from (e.g., ~/codex-backup.json)"),
|
|
1229
|
+
},
|
|
1230
|
+
async execute({ path: filePath }) {
|
|
1231
|
+
try {
|
|
1232
|
+
const result = await importAccounts(filePath);
|
|
1233
|
+
cachedAccountManager = null;
|
|
1234
|
+
const lines = [`Import complete.`, ``];
|
|
1235
|
+
if (result.imported > 0) {
|
|
1236
|
+
lines.push(`New accounts: ${result.imported}`);
|
|
1237
|
+
}
|
|
1238
|
+
if (result.skipped > 0) {
|
|
1239
|
+
lines.push(`Duplicates skipped: ${result.skipped}`);
|
|
1240
|
+
}
|
|
1241
|
+
lines.push(`Total accounts: ${result.total}`);
|
|
1242
|
+
return lines.join("\n");
|
|
1243
|
+
}
|
|
1244
|
+
catch (error) {
|
|
1245
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
1246
|
+
return `Import failed: ${msg}`;
|
|
1247
|
+
}
|
|
1248
|
+
},
|
|
1249
|
+
}),
|
|
1178
1250
|
},
|
|
1179
1251
|
};
|
|
1180
1252
|
};
|