oc-chatgpt-multi-auth 5.4.5 → 5.4.6
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 +2 -1
- package/config/README.md +5 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +105 -15
- package/dist/index.js.map +1 -1
- package/dist/lib/request/fetch-helpers.d.ts +1 -0
- package/dist/lib/request/fetch-helpers.d.ts.map +1 -1
- package/dist/lib/request/fetch-helpers.js +40 -0
- package/dist/lib/request/fetch-helpers.js.map +1 -1
- package/dist/lib/storage.d.ts +5 -0
- package/dist/lib/storage.d.ts.map +1 -1
- package/dist/lib/storage.js +57 -23
- package/dist/lib/storage.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -32,6 +32,7 @@ What the installer does:
|
|
|
32
32
|
|
|
33
33
|
- writes `~/.config/opencode/opencode.json`
|
|
34
34
|
- backs up an existing config before changing it
|
|
35
|
+
- normalizes the plugin entry to `"oc-chatgpt-multi-auth"`
|
|
35
36
|
- clears the cached plugin copy so OpenCode reinstalls the latest package
|
|
36
37
|
|
|
37
38
|
## Example Usage
|
|
@@ -120,7 +121,7 @@ Start here if the plugin does not load or authenticate correctly:
|
|
|
120
121
|
|
|
121
122
|
Common first checks:
|
|
122
123
|
|
|
123
|
-
- confirm `"plugin": ["oc-chatgpt-multi-auth
|
|
124
|
+
- confirm `"plugin": ["oc-chatgpt-multi-auth"]` is present in your OpenCode config
|
|
124
125
|
- rerun `opencode auth login`
|
|
125
126
|
- inspect `~/.opencode/logs/codex-plugin/` after running one request with `ENABLE_PLUGIN_REQUEST_LOGGING=1`
|
|
126
127
|
|
package/config/README.md
CHANGED
|
@@ -6,8 +6,8 @@ This directory contains the official OpenCode config templates for the ChatGPT C
|
|
|
6
6
|
|
|
7
7
|
| File | OpenCode version | Description |
|
|
8
8
|
|------|------------------|-------------|
|
|
9
|
-
| [`opencode-modern.json`](./opencode-modern.json) | **v1.0.210+** | Variant-based config:
|
|
10
|
-
| [`opencode-legacy.json`](./opencode-legacy.json) | **v1.0.209 and below** | Legacy explicit entries:
|
|
9
|
+
| [`opencode-modern.json`](./opencode-modern.json) | **v1.0.210+** | Variant-based config: 7 base models with 26 total presets |
|
|
10
|
+
| [`opencode-legacy.json`](./opencode-legacy.json) | **v1.0.209 and below** | Legacy explicit entries: 26 individual model definitions |
|
|
11
11
|
|
|
12
12
|
## Quick pick
|
|
13
13
|
|
|
@@ -34,11 +34,13 @@ opencode --version
|
|
|
34
34
|
OpenCode v1.0.210+ added model `variants`, so one model entry can expose multiple reasoning levels. That keeps modern config much smaller while preserving the same effective presets.
|
|
35
35
|
|
|
36
36
|
Both templates include:
|
|
37
|
-
- GPT-5.4, GPT-5 Codex, GPT-5.1, GPT-5.1 Codex, GPT-5.1 Codex Max, GPT-5.1 Codex Mini
|
|
37
|
+
- GPT-5.4, GPT-5.4 Mini, GPT-5 Codex, GPT-5.1, GPT-5.1 Codex, GPT-5.1 Codex Max, GPT-5.1 Codex Mini
|
|
38
38
|
- Reasoning variants per model family
|
|
39
39
|
- `store: false` and `include: ["reasoning.encrypted_content"]`
|
|
40
40
|
- Context metadata (`gpt-5.4*`: 1,000,000 context / 128,000 output; other shipped models: 272,000 / 128,000)
|
|
41
41
|
|
|
42
|
+
Use `opencode debug config` to verify that these template entries were merged into your effective config. `opencode models openai` currently shows OpenCode's built-in provider catalog and can omit config-defined entries such as `gpt-5.4-mini`.
|
|
43
|
+
|
|
42
44
|
If your OpenCode runtime supports global compaction tuning, you can also set:
|
|
43
45
|
- `model_context_window = 1000000`
|
|
44
46
|
- `model_auto_compact_token_limit = 900000`
|
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;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAGH,OAAO,KAAK,EAAE,MAAM,EAAe,MAAM,qBAAqB,CAAC;AA4M/D;;;;;;;;;;;;;;;GAeG;AAEH,eAAO,MAAM,iBAAiB,EAAE,MAsxL/B,CAAC;AAEF,eAAO,MAAM,gBAAgB,QAAoB,CAAC;AAElD,eAAe,iBAAiB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -34,8 +34,8 @@ import { initLogger, logRequest, logDebug, logInfo, logWarn, logError, setCorrel
|
|
|
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, selectBestAccountCandidate, shouldUpdateAccountIdFromToken, resolveRequestAccountId, parseRateLimitReason, lookupCodexCliTokensByEmail, } from "./lib/accounts.js";
|
|
37
|
-
import { getStoragePath, loadAccounts, saveAccounts, withAccountStorageTransaction, clearAccounts, setStoragePath, exportAccounts, importAccounts, previewImportAccounts, createTimestampedBackupPath, loadFlaggedAccounts, saveFlaggedAccounts, clearFlaggedAccounts, StorageError, formatStorageErrorHint, } from "./lib/storage.js";
|
|
38
|
-
import { createCodexHeaders, extractRequestUrl, handleErrorResponse, handleSuccessResponse, getUnsupportedCodexModelInfo, resolveUnsupportedCodexFallbackModel, refreshAndUpdateToken, rewriteUrlForCodex, shouldRefreshToken, transformRequestForCodex, } from "./lib/request/fetch-helpers.js";
|
|
37
|
+
import { getStoragePath, loadAccounts, saveAccounts, withAccountStorageTransaction, clearAccounts, setStoragePath, exportAccounts, importAccounts, previewImportAccounts, createTimestampedBackupPath, loadFlaggedAccounts, saveFlaggedAccounts, withFlaggedAccountStorageTransaction, clearFlaggedAccounts, StorageError, formatStorageErrorHint, } from "./lib/storage.js";
|
|
38
|
+
import { createCodexHeaders, extractRequestUrl, handleErrorResponse, handleSuccessResponse, isDeactivatedWorkspaceError, getUnsupportedCodexModelInfo, resolveUnsupportedCodexFallbackModel, refreshAndUpdateToken, rewriteUrlForCodex, shouldRefreshToken, transformRequestForCodex, } from "./lib/request/fetch-helpers.js";
|
|
39
39
|
import { applyFastSessionDefaults } from "./lib/request/request-transformer.js";
|
|
40
40
|
import { getRateLimitBackoff, RATE_LIMIT_SHORT_RETRY_THRESHOLD_MS, resetRateLimitBackoff, } from "./lib/request/rate-limit-backoff.js";
|
|
41
41
|
import { isEmptyResponse } from "./lib/request/response-handler.js";
|
|
@@ -48,6 +48,31 @@ import { buildBeginnerChecklist, buildBeginnerDoctorFindings, recommendBeginnerN
|
|
|
48
48
|
import { getModelFamily, getCodexInstructions, MODEL_FAMILIES, prewarmCodexInstructions, } from "./lib/prompts/codex.js";
|
|
49
49
|
import { prewarmOpenCodeCodexPrompt } from "./lib/prompts/opencode-codex.js";
|
|
50
50
|
import { createSessionRecoveryHook, isRecoverableError, detectErrorType, getRecoveryToastContent, } from "./lib/recovery.js";
|
|
51
|
+
function getWorkspaceIdentityKey(account) {
|
|
52
|
+
const organizationId = account.organizationId?.trim();
|
|
53
|
+
const accountId = account.accountId?.trim();
|
|
54
|
+
const refreshToken = account.refreshToken.trim();
|
|
55
|
+
if (organizationId) {
|
|
56
|
+
return accountId
|
|
57
|
+
? `organizationId:${organizationId}|accountId:${accountId}`
|
|
58
|
+
: `organizationId:${organizationId}`;
|
|
59
|
+
}
|
|
60
|
+
if (accountId)
|
|
61
|
+
return `accountId:${accountId}`;
|
|
62
|
+
return `refreshToken:${refreshToken}`;
|
|
63
|
+
}
|
|
64
|
+
function matchesWorkspaceIdentity(account, identityKey) {
|
|
65
|
+
return getWorkspaceIdentityKey(account) === identityKey;
|
|
66
|
+
}
|
|
67
|
+
function upsertFlaggedAccountRecord(accounts, record) {
|
|
68
|
+
const identityKey = getWorkspaceIdentityKey(record);
|
|
69
|
+
const existingIndex = accounts.findIndex((flagged) => matchesWorkspaceIdentity(flagged, identityKey));
|
|
70
|
+
if (existingIndex >= 0) {
|
|
71
|
+
accounts[existingIndex] = record;
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
accounts.push(record);
|
|
75
|
+
}
|
|
51
76
|
/**
|
|
52
77
|
* OpenAI Codex OAuth authentication plugin for opencode
|
|
53
78
|
*
|
|
@@ -1737,6 +1762,46 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
1737
1762
|
requestCorrelationId,
|
|
1738
1763
|
threadId: threadIdCandidate,
|
|
1739
1764
|
});
|
|
1765
|
+
const workspaceDeactivated = isDeactivatedWorkspaceError(errorBody, response.status);
|
|
1766
|
+
if (workspaceDeactivated) {
|
|
1767
|
+
const accountLabel = formatAccountLabel(account, account.index);
|
|
1768
|
+
accountManager.refundToken(account, modelFamily, model);
|
|
1769
|
+
accountManager.recordFailure(account, modelFamily, model);
|
|
1770
|
+
account.lastSwitchReason = "rotation";
|
|
1771
|
+
runtimeMetrics.failedRequests++;
|
|
1772
|
+
runtimeMetrics.accountRotations++;
|
|
1773
|
+
runtimeMetrics.lastError = `Deactivated workspace on ${accountLabel}`;
|
|
1774
|
+
runtimeMetrics.lastErrorCategory = "workspace-deactivated";
|
|
1775
|
+
try {
|
|
1776
|
+
const flaggedRecord = {
|
|
1777
|
+
...account,
|
|
1778
|
+
flaggedAt: Date.now(),
|
|
1779
|
+
flaggedReason: "workspace-deactivated",
|
|
1780
|
+
lastError: "deactivated_workspace",
|
|
1781
|
+
};
|
|
1782
|
+
await withFlaggedAccountStorageTransaction(async (current, persist) => {
|
|
1783
|
+
const nextStorage = {
|
|
1784
|
+
...current,
|
|
1785
|
+
accounts: current.accounts.map((flagged) => ({ ...flagged })),
|
|
1786
|
+
};
|
|
1787
|
+
upsertFlaggedAccountRecord(nextStorage.accounts, flaggedRecord);
|
|
1788
|
+
await persist(nextStorage);
|
|
1789
|
+
});
|
|
1790
|
+
}
|
|
1791
|
+
catch (flagError) {
|
|
1792
|
+
logWarn(`Failed to persist deactivated workspace flag for ${accountLabel}: ${flagError instanceof Error ? flagError.message : String(flagError)}`);
|
|
1793
|
+
}
|
|
1794
|
+
if (accountManager.removeAccount(account)) {
|
|
1795
|
+
accountManager.saveToDiskDebounced();
|
|
1796
|
+
attempted.clear();
|
|
1797
|
+
accountCount = accountManager.getAccountCount();
|
|
1798
|
+
await showToast(`Workspace deactivated. Removed ${accountLabel} from rotation and switching accounts.`, "warning", { duration: toastDurationMs });
|
|
1799
|
+
break;
|
|
1800
|
+
}
|
|
1801
|
+
accountManager.markAccountCoolingDown(account, ACCOUNT_LIMITS.AUTH_FAILURE_COOLDOWN_MS, "auth-failure");
|
|
1802
|
+
accountManager.saveToDiskDebounced();
|
|
1803
|
+
break;
|
|
1804
|
+
}
|
|
1740
1805
|
const unsupportedModelInfo = getUnsupportedCodexModelInfo(errorBody);
|
|
1741
1806
|
const hasRemainingAccounts = attempted.size < Math.max(1, accountCount);
|
|
1742
1807
|
// Entitlements can differ by account/workspace, so try remaining
|
|
@@ -2204,12 +2269,18 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
2204
2269
|
const message = (typeof errorBody?.error?.message === "string"
|
|
2205
2270
|
? errorBody.error?.message
|
|
2206
2271
|
: bodyText) || `HTTP ${response.status}`;
|
|
2272
|
+
if (isDeactivatedWorkspaceError(errorBody, response.status)) {
|
|
2273
|
+
throw new Error("deactivated_workspace");
|
|
2274
|
+
}
|
|
2207
2275
|
throw new Error(message);
|
|
2208
2276
|
}
|
|
2209
2277
|
lastError = new Error("Codex response did not include quota headers");
|
|
2210
2278
|
}
|
|
2211
2279
|
catch (error) {
|
|
2212
2280
|
lastError = error instanceof Error ? error : new Error(String(error));
|
|
2281
|
+
if (lastError.message === "deactivated_workspace") {
|
|
2282
|
+
throw lastError;
|
|
2283
|
+
}
|
|
2213
2284
|
}
|
|
2214
2285
|
}
|
|
2215
2286
|
throw lastError ?? new Error("Failed to fetch quotas");
|
|
@@ -2229,9 +2300,9 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
2229
2300
|
console.log("\nNo accounts to check.\n");
|
|
2230
2301
|
return;
|
|
2231
2302
|
}
|
|
2232
|
-
const flaggedStorage = await loadFlaggedAccounts();
|
|
2233
2303
|
let storageChanged = false;
|
|
2234
2304
|
let flaggedChanged = false;
|
|
2305
|
+
const flaggedUpdates = new Map();
|
|
2235
2306
|
const removeFromActive = new Set();
|
|
2236
2307
|
const total = workingStorage.accounts.length;
|
|
2237
2308
|
let ok = 0;
|
|
@@ -2315,20 +2386,14 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
2315
2386
|
const message = refreshResult.message ?? refreshResult.reason ?? "refresh failed";
|
|
2316
2387
|
console.log(`[${i + 1}/${total}] ${label}: ERROR (${message})`);
|
|
2317
2388
|
if (deepProbe && isFlaggableFailure(refreshResult)) {
|
|
2318
|
-
const existingIndex = flaggedStorage.accounts.findIndex((flagged) => flagged.refreshToken === account.refreshToken);
|
|
2319
2389
|
const flaggedRecord = {
|
|
2320
2390
|
...account,
|
|
2321
2391
|
flaggedAt: Date.now(),
|
|
2322
2392
|
flaggedReason: "token-invalid",
|
|
2323
2393
|
lastError: message,
|
|
2324
2394
|
};
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
}
|
|
2328
|
-
else {
|
|
2329
|
-
flaggedStorage.accounts.push(flaggedRecord);
|
|
2330
|
-
}
|
|
2331
|
-
removeFromActive.add(account.refreshToken);
|
|
2395
|
+
flaggedUpdates.set(getWorkspaceIdentityKey(flaggedRecord), flaggedRecord);
|
|
2396
|
+
removeFromActive.add(getWorkspaceIdentityKey(account));
|
|
2332
2397
|
flaggedChanged = true;
|
|
2333
2398
|
}
|
|
2334
2399
|
continue;
|
|
@@ -2391,6 +2456,17 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
2391
2456
|
catch (error) {
|
|
2392
2457
|
errors += 1;
|
|
2393
2458
|
const message = error instanceof Error ? error.message : String(error);
|
|
2459
|
+
if (message === "deactivated_workspace") {
|
|
2460
|
+
const flaggedRecord = {
|
|
2461
|
+
...account,
|
|
2462
|
+
flaggedAt: Date.now(),
|
|
2463
|
+
flaggedReason: "workspace-deactivated",
|
|
2464
|
+
lastError: message,
|
|
2465
|
+
};
|
|
2466
|
+
flaggedUpdates.set(getWorkspaceIdentityKey(flaggedRecord), flaggedRecord);
|
|
2467
|
+
removeFromActive.add(getWorkspaceIdentityKey(account));
|
|
2468
|
+
flaggedChanged = true;
|
|
2469
|
+
}
|
|
2394
2470
|
console.log(`[${i + 1}/${total}] ${label}: ERROR (${message.slice(0, 160)})`);
|
|
2395
2471
|
}
|
|
2396
2472
|
}
|
|
@@ -2401,7 +2477,7 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
2401
2477
|
}
|
|
2402
2478
|
}
|
|
2403
2479
|
if (removeFromActive.size > 0) {
|
|
2404
|
-
workingStorage.accounts = workingStorage.accounts.filter((account) => !removeFromActive.has(account
|
|
2480
|
+
workingStorage.accounts = workingStorage.accounts.filter((account) => !removeFromActive.has(getWorkspaceIdentityKey(account)));
|
|
2405
2481
|
clampActiveIndices(workingStorage);
|
|
2406
2482
|
storageChanged = true;
|
|
2407
2483
|
}
|
|
@@ -2410,12 +2486,21 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
2410
2486
|
invalidateAccountManagerCache();
|
|
2411
2487
|
}
|
|
2412
2488
|
if (flaggedChanged) {
|
|
2413
|
-
await
|
|
2489
|
+
await withFlaggedAccountStorageTransaction(async (current, persist) => {
|
|
2490
|
+
const nextStorage = {
|
|
2491
|
+
...current,
|
|
2492
|
+
accounts: current.accounts.map((flagged) => ({ ...flagged })),
|
|
2493
|
+
};
|
|
2494
|
+
for (const flaggedRecord of flaggedUpdates.values()) {
|
|
2495
|
+
upsertFlaggedAccountRecord(nextStorage.accounts, flaggedRecord);
|
|
2496
|
+
}
|
|
2497
|
+
await persist(nextStorage);
|
|
2498
|
+
});
|
|
2414
2499
|
}
|
|
2415
2500
|
console.log("");
|
|
2416
2501
|
console.log(`Results: ${ok} ok, ${errors} error, ${disabled} disabled`);
|
|
2417
2502
|
if (removeFromActive.size > 0) {
|
|
2418
|
-
console.log(`Moved ${removeFromActive.size} account(s) to flagged pool
|
|
2503
|
+
console.log(`Moved ${removeFromActive.size} account(s) to flagged pool.`);
|
|
2419
2504
|
}
|
|
2420
2505
|
console.log("");
|
|
2421
2506
|
};
|
|
@@ -2433,6 +2518,11 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
2433
2518
|
if (!flagged)
|
|
2434
2519
|
continue;
|
|
2435
2520
|
const label = flagged.email ?? flagged.accountLabel ?? `Flagged ${i + 1}`;
|
|
2521
|
+
if (flagged.flaggedReason === "workspace-deactivated") {
|
|
2522
|
+
console.log(`[${i + 1}/${flaggedStorage.accounts.length}] ${label}: STILL FLAGGED (workspace deactivated)`);
|
|
2523
|
+
remaining.push(flagged);
|
|
2524
|
+
continue;
|
|
2525
|
+
}
|
|
2436
2526
|
try {
|
|
2437
2527
|
const cached = await lookupCodexCliTokensByEmail(flagged.email);
|
|
2438
2528
|
const now = Date.now();
|
|
@@ -2589,7 +2679,7 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
2589
2679
|
await saveAccounts(workingStorage);
|
|
2590
2680
|
await saveFlaggedAccounts({
|
|
2591
2681
|
version: 1,
|
|
2592
|
-
accounts: flaggedStorage.accounts.filter((flagged) => flagged
|
|
2682
|
+
accounts: flaggedStorage.accounts.filter((flagged) => !matchesWorkspaceIdentity(flagged, getWorkspaceIdentityKey(target))),
|
|
2593
2683
|
});
|
|
2594
2684
|
invalidateAccountManagerCache();
|
|
2595
2685
|
console.log(`\nDeleted ${target.email ?? `Account ${menuResult.deleteAccountIndex + 1}`}.\n`);
|