oc-codex-multi-auth 6.4.0 → 6.5.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/README.md +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +15 -14
- package/dist/index.js.map +1 -1
- package/dist/lib/accounts/rotation.d.ts +13 -0
- package/dist/lib/accounts/rotation.d.ts.map +1 -1
- package/dist/lib/accounts/rotation.js +43 -37
- package/dist/lib/accounts/rotation.js.map +1 -1
- package/dist/lib/accounts/warm-request.d.ts +25 -6
- package/dist/lib/accounts/warm-request.d.ts.map +1 -1
- package/dist/lib/accounts/warm-request.js +67 -15
- package/dist/lib/accounts/warm-request.js.map +1 -1
- package/dist/lib/constants.d.ts +0 -11
- package/dist/lib/constants.d.ts.map +1 -1
- package/dist/lib/constants.js +0 -11
- package/dist/lib/constants.js.map +1 -1
- package/dist/lib/rotation.d.ts +15 -0
- package/dist/lib/rotation.d.ts.map +1 -1
- package/dist/lib/rotation.js +26 -0
- package/dist/lib/rotation.js.map +1 -1
- package/dist/lib/tools/codex-warm.d.ts.map +1 -1
- package/dist/lib/tools/codex-warm.js +7 -1
- package/dist/lib/tools/codex-warm.js.map +1 -1
- package/package.json +1 -1
- package/scripts/install-oc-codex-multi-auth-core.js +126 -3
- package/scripts/install-oc-codex-multi-auth.js +15 -5
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "oc-codex-multi-auth",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.5.0",
|
|
4
4
|
"description": "OpenCode plugin for ChatGPT Plus/Pro OAuth with Codex/GPT-5 routing, multi-account rotation, account switching, health checks, diagnostics, and recovery tools",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -2,7 +2,7 @@ import { existsSync, realpathSync } from "node:fs";
|
|
|
2
2
|
import { copyFile, mkdir, readFile, rename, rm, writeFile } from "node:fs/promises";
|
|
3
3
|
import { homedir } from "node:os";
|
|
4
4
|
import { dirname, join, resolve } from "node:path";
|
|
5
|
-
import { fileURLToPath } from "node:url";
|
|
5
|
+
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
6
6
|
|
|
7
7
|
const PACKAGE_NAME = "oc-codex-multi-auth";
|
|
8
8
|
const LEGACY_PACKAGE_NAMES = ["oc-chatgpt-multi-auth"];
|
|
@@ -13,7 +13,7 @@ const STALE_MANAGED_MODEL_KEYS = new Set([
|
|
|
13
13
|
"gpt-5.3-codex",
|
|
14
14
|
"gpt-5.4",
|
|
15
15
|
]);
|
|
16
|
-
const STANDALONE_COMMANDS = new Set(["doctor", "status", "list", "limits", "dashboard", "health", "diag"]);
|
|
16
|
+
const STANDALONE_COMMANDS = new Set(["doctor", "status", "list", "limits", "dashboard", "health", "diag", "warm"]);
|
|
17
17
|
const INSTALLER_COMMANDS = new Set(["install"]);
|
|
18
18
|
|
|
19
19
|
function splitCommandArgv(argv) {
|
|
@@ -83,7 +83,8 @@ function printHelp() {
|
|
|
83
83
|
" limits Show stored rate-limit state\n" +
|
|
84
84
|
" dashboard Print dashboard guidance\n" +
|
|
85
85
|
" health Check local token/account health\n" +
|
|
86
|
-
" diag Alias for doctor --deep\n
|
|
86
|
+
" diag Alias for doctor --deep\n" +
|
|
87
|
+
" warm Open every enabled account's usage window now (one request each)\n\n" +
|
|
87
88
|
`Installer usage: ${PACKAGE_NAME} [--modern|--full|--legacy] [--dry-run] [--no-cache-clear]\n\n` +
|
|
88
89
|
"Default behavior:\n" +
|
|
89
90
|
" - Installs/updates global config at ~/.config/opencode/opencode.json\n" +
|
|
@@ -352,6 +353,125 @@ function printStandaloneResult(command, payload, json) {
|
|
|
352
353
|
if (payload.nextAction) console.log(`Next: ${payload.nextAction}`);
|
|
353
354
|
}
|
|
354
355
|
|
|
356
|
+
async function loadWarmRuntime(env) {
|
|
357
|
+
// Reuse the COMPILED warm logic from dist/ so the standalone CLI behaves
|
|
358
|
+
// identically to the in-conversation codex-warm tool. dist/ ships in the
|
|
359
|
+
// npm package (files allowlist) and none of these modules import the
|
|
360
|
+
// OpenCode plugin runtime, so they load cleanly in plain Node.
|
|
361
|
+
const distRoot = join(repoRoot, "dist", "lib");
|
|
362
|
+
const toUrl = (rel) => pathToFileURL(join(distRoot, rel)).href;
|
|
363
|
+
try {
|
|
364
|
+
const [storageMod, usageMod, warmReqMod, warmMod] = await Promise.all([
|
|
365
|
+
import(toUrl("storage.js")),
|
|
366
|
+
import(toUrl("codex-usage.js")),
|
|
367
|
+
import(toUrl("accounts/warm-request.js")),
|
|
368
|
+
import(toUrl("accounts/warm.js")),
|
|
369
|
+
]);
|
|
370
|
+
return { storageMod, usageMod, warmReqMod, warmMod };
|
|
371
|
+
} catch (error) {
|
|
372
|
+
throw new Error(
|
|
373
|
+
`Could not load warm runtime from dist/. Build the package first (npm run build). Cause: ${formatErrorForLog(error)}`,
|
|
374
|
+
);
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
export async function runWarmCommand(parsed, options = {}) {
|
|
379
|
+
const { env = process.env } = options;
|
|
380
|
+
const storagePath = getStandaloneStoragePath(parsed, env);
|
|
381
|
+
|
|
382
|
+
let runtime;
|
|
383
|
+
try {
|
|
384
|
+
runtime = await loadWarmRuntime(env);
|
|
385
|
+
} catch (error) {
|
|
386
|
+
const payload = { command: "warm", storagePath, error: formatErrorForLog(error) };
|
|
387
|
+
printWarmResult(payload, parsed.json);
|
|
388
|
+
return { exitCode: 1, action: "warm", storagePath };
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
const { storageMod, usageMod, warmReqMod, warmMod } = runtime;
|
|
392
|
+
// Point dist storage at the resolved accounts file so a refreshed token is
|
|
393
|
+
// persisted to the SAME file the rest of the toolchain reads.
|
|
394
|
+
storageMod.setStoragePathDirect(storagePath);
|
|
395
|
+
|
|
396
|
+
const storage = await storageMod.loadAccounts();
|
|
397
|
+
const accounts = Array.isArray(storage?.accounts) ? storage.accounts : [];
|
|
398
|
+
if (accounts.length === 0) {
|
|
399
|
+
const payload = {
|
|
400
|
+
command: "warm",
|
|
401
|
+
storagePath,
|
|
402
|
+
totalAccounts: 0,
|
|
403
|
+
warmed: 0,
|
|
404
|
+
failed: 0,
|
|
405
|
+
skipped: 0,
|
|
406
|
+
results: [],
|
|
407
|
+
message: "No accounts configured.",
|
|
408
|
+
nextAction: "Run opencode auth login.",
|
|
409
|
+
};
|
|
410
|
+
printWarmResult(payload, parsed.json);
|
|
411
|
+
return { exitCode: 0, action: "warm", storagePath };
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
// Same adapter as lib/tools/codex-warm.ts createWarmOne: refresh → resolve
|
|
415
|
+
// account id → open the usage window; map an exhausted (quota-429) account
|
|
416
|
+
// to a failure so it is not reported as warmed.
|
|
417
|
+
const warmOne = async (account) => {
|
|
418
|
+
const { accessToken } = await usageMod.ensureCodexUsageAccessToken({ storage, account });
|
|
419
|
+
const accountId = usageMod.resolveCodexUsageAccountId({ account, accessToken });
|
|
420
|
+
if (!accountId) {
|
|
421
|
+
return { status: "failed", detail: "could not resolve account id (re-login may be required)" };
|
|
422
|
+
}
|
|
423
|
+
const result = await warmReqMod.warmAccountWindow({
|
|
424
|
+
accountId,
|
|
425
|
+
accessToken,
|
|
426
|
+
organizationId: account.organizationId,
|
|
427
|
+
});
|
|
428
|
+
if (result.status === "exhausted") {
|
|
429
|
+
return { status: "failed", detail: result.detail ?? "quota/usage limit reached" };
|
|
430
|
+
}
|
|
431
|
+
return { status: "warmed" };
|
|
432
|
+
};
|
|
433
|
+
|
|
434
|
+
const summary = await warmMod.warmAccounts(accounts, warmOne);
|
|
435
|
+
const payload = {
|
|
436
|
+
command: "warm",
|
|
437
|
+
storagePath,
|
|
438
|
+
totalAccounts: summary.total,
|
|
439
|
+
warmed: summary.warmedCount,
|
|
440
|
+
failed: summary.failedCount,
|
|
441
|
+
skipped: summary.skippedCount,
|
|
442
|
+
results: summary.results.map((r) => ({
|
|
443
|
+
index: r.index,
|
|
444
|
+
email: maskValue(accounts[r.index]?.email, parsed.includeSensitive),
|
|
445
|
+
status: r.status,
|
|
446
|
+
detail: r.detail,
|
|
447
|
+
})),
|
|
448
|
+
};
|
|
449
|
+
printWarmResult(payload, parsed.json);
|
|
450
|
+
return { exitCode: summary.failedCount > 0 ? 1 : 0, action: "warm", storagePath };
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
function printWarmResult(payload, json) {
|
|
454
|
+
if (json) {
|
|
455
|
+
console.log(JSON.stringify(payload, null, 2));
|
|
456
|
+
return;
|
|
457
|
+
}
|
|
458
|
+
console.log(`oc-codex-multi-auth warm`);
|
|
459
|
+
if (payload.message) console.log(payload.message);
|
|
460
|
+
console.log(`Storage: ${payload.storagePath}`);
|
|
461
|
+
if (payload.error) {
|
|
462
|
+
console.log(`Error: ${payload.error}`);
|
|
463
|
+
return;
|
|
464
|
+
}
|
|
465
|
+
console.log(`Accounts: ${payload.totalAccounts}`);
|
|
466
|
+
for (const r of payload.results ?? []) {
|
|
467
|
+
const label = r.email ? `[${r.index}] ${r.email}` : `[${r.index}]`;
|
|
468
|
+
const detail = r.detail ? ` — ${r.detail}` : "";
|
|
469
|
+
console.log(`- ${label}: ${r.status}${detail}`);
|
|
470
|
+
}
|
|
471
|
+
console.log(`Summary: ${payload.warmed} warmed, ${payload.failed} failed, ${payload.skipped} skipped`);
|
|
472
|
+
if (payload.nextAction) console.log(`Next: ${payload.nextAction}`);
|
|
473
|
+
}
|
|
474
|
+
|
|
355
475
|
export async function runStandaloneCommand(command, argv = [], options = {}) {
|
|
356
476
|
const parsed = parseStandaloneArgs(argv);
|
|
357
477
|
if (command === "diag") {
|
|
@@ -362,6 +482,9 @@ export async function runStandaloneCommand(command, argv = [], options = {}) {
|
|
|
362
482
|
printHelp();
|
|
363
483
|
return { exitCode: 0, action: "help" };
|
|
364
484
|
}
|
|
485
|
+
if (command === "warm") {
|
|
486
|
+
return runWarmCommand(parsed, options);
|
|
487
|
+
}
|
|
365
488
|
const { env = process.env } = options;
|
|
366
489
|
const storagePath = getStandaloneStoragePath(parsed, env);
|
|
367
490
|
const { storage, error } = await readStandaloneStorage(storagePath);
|
|
@@ -19,9 +19,19 @@ export function isDirectRunPath(
|
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
if (isDirectRunPath()) {
|
|
22
|
-
runInstaller()
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
22
|
+
runInstaller()
|
|
23
|
+
.then((result) => {
|
|
24
|
+
// Propagate a non-zero exit from standalone commands (e.g. `warm`
|
|
25
|
+
// reporting per-account failures) without throwing. Read-only
|
|
26
|
+
// commands return exitCode 0; only set a failure code so a clean run
|
|
27
|
+
// still exits 0.
|
|
28
|
+
if (result && typeof result.exitCode === "number" && result.exitCode !== 0) {
|
|
29
|
+
process.exitCode = result.exitCode;
|
|
30
|
+
}
|
|
31
|
+
})
|
|
32
|
+
.catch((error) => {
|
|
33
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
34
|
+
console.error(`Installer failed: ${message}`);
|
|
35
|
+
process.exit(1);
|
|
36
|
+
});
|
|
27
37
|
}
|