oc-codex-multi-auth 6.4.1 → 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/lib/accounts/warm-request.d.ts +6 -0
- package/dist/lib/accounts/warm-request.d.ts.map +1 -1
- package/dist/lib/accounts/warm-request.js +16 -1
- package/dist/lib/accounts/warm-request.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/README.md
CHANGED
|
@@ -180,6 +180,8 @@ If browser launch is blocked, use the alternate login paths in [docs/getting-sta
|
|
|
180
180
|
| `codex-limits` | What quota or rate-limit state is visible now? |
|
|
181
181
|
| `codex-dashboard` | Can I manage accounts from one interactive surface? |
|
|
182
182
|
|
|
183
|
+
Most of these also run as a **direct CLI** with no agent/model involvement (no token cost) — e.g. `oc-codex-multi-auth warm`, `oc-codex-multi-auth status`, or `npx -y oc-codex-multi-auth@latest warm`. Use `oc-codex-multi-auth warm` to open every enabled account's usage window at the start of a session and stagger the rolling quota cooldowns. Add `--json` for scriptable output.
|
|
184
|
+
|
|
183
185
|
### Account management
|
|
184
186
|
|
|
185
187
|
| Tool | What it answers |
|
|
@@ -26,6 +26,12 @@ export interface WarmRequestParams {
|
|
|
26
26
|
/**
|
|
27
27
|
* Build the minimal warm-ping request body. Exported for tests so the exact
|
|
28
28
|
* shape (stream/store/reasoning) stays pinned.
|
|
29
|
+
*
|
|
30
|
+
* Instruction resolution is best-effort: a warm ping only needs a valid request
|
|
31
|
+
* that opens the usage window, not the full Codex system prompt. If
|
|
32
|
+
* `getCodexInstructions` cannot resolve the prompt (offline, cache miss, or the
|
|
33
|
+
* bundled file is unavailable in a standalone CLI run), we fall back to a
|
|
34
|
+
* minimal instruction so warming never fails on prompt-file resolution.
|
|
29
35
|
*/
|
|
30
36
|
export declare function buildWarmRequestBody(model?: string): Promise<RequestBody>;
|
|
31
37
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"warm-request.d.ts","sourceRoot":"","sources":["../../../lib/accounts/warm-request.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAMH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAW/C,MAAM,WAAW,iBAAiB;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,oCAAoC;IACpC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,4DAA4D;IAC5D,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;CACzB;AAED
|
|
1
|
+
{"version":3,"file":"warm-request.d.ts","sourceRoot":"","sources":["../../../lib/accounts/warm-request.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAMH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAW/C,MAAM,WAAW,iBAAiB;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,oCAAoC;IACpC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,4DAA4D;IAC5D,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;CACzB;AAED;;;;;;;;;GASG;AACH,wBAAsB,oBAAoB,CAAC,KAAK,SAAa,GAAG,OAAO,CAAC,WAAW,CAAC,CA0BnF;AAED;;;;;;;GAOG;AACH,MAAM,MAAM,iBAAiB,GAAG,QAAQ,GAAG,WAAW,CAAC;AAEvD,MAAM,WAAW,iBAAiB;IACjC,MAAM,EAAE,iBAAiB,CAAC;IAC1B,MAAM,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;;GAQG;AACH,wBAAsB,iBAAiB,CACtC,MAAM,EAAE,iBAAiB,GACvB,OAAO,CAAC,iBAAiB,CAAC,CA4D5B"}
|
|
@@ -26,9 +26,24 @@ const WARM_TIMEOUT_MS = 15_000;
|
|
|
26
26
|
/**
|
|
27
27
|
* Build the minimal warm-ping request body. Exported for tests so the exact
|
|
28
28
|
* shape (stream/store/reasoning) stays pinned.
|
|
29
|
+
*
|
|
30
|
+
* Instruction resolution is best-effort: a warm ping only needs a valid request
|
|
31
|
+
* that opens the usage window, not the full Codex system prompt. If
|
|
32
|
+
* `getCodexInstructions` cannot resolve the prompt (offline, cache miss, or the
|
|
33
|
+
* bundled file is unavailable in a standalone CLI run), we fall back to a
|
|
34
|
+
* minimal instruction so warming never fails on prompt-file resolution.
|
|
29
35
|
*/
|
|
30
36
|
export async function buildWarmRequestBody(model = WARM_MODEL) {
|
|
31
|
-
|
|
37
|
+
let instructions;
|
|
38
|
+
try {
|
|
39
|
+
instructions = await getCodexInstructions(model);
|
|
40
|
+
}
|
|
41
|
+
catch (error) {
|
|
42
|
+
log.debug("getCodexInstructions failed for warm ping; using minimal fallback", {
|
|
43
|
+
error: error instanceof Error ? error.message : String(error),
|
|
44
|
+
});
|
|
45
|
+
instructions = "You are a helpful assistant.";
|
|
46
|
+
}
|
|
32
47
|
return {
|
|
33
48
|
model,
|
|
34
49
|
stream: true,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"warm-request.js","sourceRoot":"","sources":["../../../lib/accounts/warm-request.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAC;AACjE,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAExD,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAE5C,MAAM,GAAG,GAAG,YAAY,CAAC,cAAc,CAAC,CAAC;AAEzC,0EAA0E;AAC1E,MAAM,UAAU,GAAG,SAAS,CAAC;AAE7B,gFAAgF;AAChF,MAAM,eAAe,GAAG,MAAM,CAAC;AAY/B
|
|
1
|
+
{"version":3,"file":"warm-request.js","sourceRoot":"","sources":["../../../lib/accounts/warm-request.ts"],"names":[],"mappings":"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"}
|
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
|
}
|