opencode-usage 0.5.4 → 0.5.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.
|
@@ -4,4 +4,9 @@
|
|
|
4
4
|
* Each command is registered via `registerCommand` so the command-runner can
|
|
5
5
|
* execute them as background jobs.
|
|
6
6
|
*/
|
|
7
|
-
|
|
7
|
+
/**
|
|
8
|
+
* Proactively refresh Codex tokens that haven't been refreshed in 24+ hours.
|
|
9
|
+
* Keeps tokens fresh so they never silently expire.
|
|
10
|
+
* Safe to call frequently — skips accounts refreshed recently.
|
|
11
|
+
*/
|
|
12
|
+
export declare function proactiveRefreshCodexTokens(): Promise<void>;
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
// @bun
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __export = (target, all) => {
|
|
5
|
+
for (var name in all)
|
|
6
|
+
__defProp(target, name, {
|
|
7
|
+
get: all[name],
|
|
8
|
+
enumerable: true,
|
|
9
|
+
configurable: true,
|
|
10
|
+
set: (newValue) => all[name] = () => newValue
|
|
11
|
+
});
|
|
12
|
+
};
|
|
3
13
|
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
4
14
|
|
|
5
15
|
// src/commander/services/command-runner.ts
|
|
@@ -232,6 +242,9 @@ var init_config_service = __esm(() => {
|
|
|
232
242
|
|
|
233
243
|
// src/commander/services/plugin-adapters.ts
|
|
234
244
|
var exports_plugin_adapters = {};
|
|
245
|
+
__export(exports_plugin_adapters, {
|
|
246
|
+
proactiveRefreshCodexTokens: () => proactiveRefreshCodexTokens
|
|
247
|
+
});
|
|
235
248
|
import { homedir as homedir6, tmpdir } from "os";
|
|
236
249
|
import { join as join8 } from "path";
|
|
237
250
|
function resolveSource2(provider) {
|
|
@@ -308,6 +321,120 @@ async function directCodexPing(alias) {
|
|
|
308
321
|
};
|
|
309
322
|
}
|
|
310
323
|
}
|
|
324
|
+
function decodeJwtPayload(token) {
|
|
325
|
+
try {
|
|
326
|
+
const parts = token.split(".");
|
|
327
|
+
if (parts.length !== 3)
|
|
328
|
+
return null;
|
|
329
|
+
const payload = Buffer.from(parts[1], "base64").toString("utf-8");
|
|
330
|
+
return JSON.parse(payload);
|
|
331
|
+
} catch {
|
|
332
|
+
return null;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
function getExpiryFromJwt(claims) {
|
|
336
|
+
if (!claims || typeof claims.exp !== "number")
|
|
337
|
+
return null;
|
|
338
|
+
return claims.exp * 1000;
|
|
339
|
+
}
|
|
340
|
+
function getAccountIdFromJwt(claims) {
|
|
341
|
+
const auth = claims?.["https://api.openai.com/auth"];
|
|
342
|
+
return auth?.chatgpt_account_id ?? null;
|
|
343
|
+
}
|
|
344
|
+
async function refreshSingleCodexToken(alias, account, storePath) {
|
|
345
|
+
const refreshToken = account.refreshToken;
|
|
346
|
+
if (typeof refreshToken !== "string" || !refreshToken) {
|
|
347
|
+
console.log(`[proactiveRefresh] ${alias}: no refresh token, skipping`);
|
|
348
|
+
return false;
|
|
349
|
+
}
|
|
350
|
+
try {
|
|
351
|
+
const t0 = Date.now();
|
|
352
|
+
const res = await fetch(CODEX_TOKEN_URL, {
|
|
353
|
+
method: "POST",
|
|
354
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
355
|
+
body: new URLSearchParams({
|
|
356
|
+
grant_type: "refresh_token",
|
|
357
|
+
client_id: CODEX_CLIENT_ID,
|
|
358
|
+
refresh_token: refreshToken
|
|
359
|
+
})
|
|
360
|
+
});
|
|
361
|
+
if (!res.ok) {
|
|
362
|
+
console.log(`[proactiveRefresh] ${alias}: refresh failed HTTP ${res.status} in ${Date.now() - t0}ms`);
|
|
363
|
+
return false;
|
|
364
|
+
}
|
|
365
|
+
const tokens = await res.json();
|
|
366
|
+
const accessClaims = decodeJwtPayload(tokens.access_token);
|
|
367
|
+
const idClaims = tokens.id_token ? decodeJwtPayload(tokens.id_token) : null;
|
|
368
|
+
const expiresAt = getExpiryFromJwt(accessClaims) ?? getExpiryFromJwt(idClaims) ?? Date.now() + tokens.expires_in * 1000;
|
|
369
|
+
const freshStore = JSON.parse(await Bun.file(storePath).text());
|
|
370
|
+
const freshAccounts = freshStore.accounts ?? {};
|
|
371
|
+
const freshAcct = freshAccounts[alias];
|
|
372
|
+
if (freshAcct) {
|
|
373
|
+
freshAcct.accessToken = tokens.access_token;
|
|
374
|
+
if (tokens.refresh_token)
|
|
375
|
+
freshAcct.refreshToken = tokens.refresh_token;
|
|
376
|
+
if (tokens.id_token)
|
|
377
|
+
freshAcct.idToken = tokens.id_token;
|
|
378
|
+
freshAcct.expiresAt = expiresAt;
|
|
379
|
+
freshAcct.lastRefresh = new Date().toISOString();
|
|
380
|
+
freshAcct.accountId = getAccountIdFromJwt(idClaims) ?? getAccountIdFromJwt(accessClaims) ?? freshAcct.accountId;
|
|
381
|
+
freshAcct.authInvalid = false;
|
|
382
|
+
}
|
|
383
|
+
await Bun.write(storePath, JSON.stringify(freshStore, null, 2));
|
|
384
|
+
const daysLeft = ((expiresAt - Date.now()) / (24 * 60 * 60 * 1000)).toFixed(1);
|
|
385
|
+
console.log(`[proactiveRefresh] ${alias}: refreshed in ${Date.now() - t0}ms, new expiry in ${daysLeft}d`);
|
|
386
|
+
return true;
|
|
387
|
+
} catch (err) {
|
|
388
|
+
console.log(`[proactiveRefresh] ${alias}: error \u2014 ${err instanceof Error ? err.message : String(err)}`);
|
|
389
|
+
return false;
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
async function proactiveRefreshCodexTokens() {
|
|
393
|
+
const STORE_PATHS = [
|
|
394
|
+
join8(homedir6(), ".config", "opencode", "codex-multi-account-accounts.json"),
|
|
395
|
+
join8(homedir6(), ".config", "opencode", "codex-multi-accounts.json"),
|
|
396
|
+
join8(homedir6(), ".config", "oc-codex-multi-account", "accounts.json")
|
|
397
|
+
];
|
|
398
|
+
let storePath = null;
|
|
399
|
+
let store = null;
|
|
400
|
+
for (const p of STORE_PATHS) {
|
|
401
|
+
try {
|
|
402
|
+
store = JSON.parse(await Bun.file(p).text());
|
|
403
|
+
storePath = p;
|
|
404
|
+
break;
|
|
405
|
+
} catch {
|
|
406
|
+
continue;
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
if (!store || !storePath)
|
|
410
|
+
return;
|
|
411
|
+
const accounts = store.accounts ?? {};
|
|
412
|
+
const now = Date.now();
|
|
413
|
+
let refreshed = 0;
|
|
414
|
+
for (const [alias, account] of Object.entries(accounts)) {
|
|
415
|
+
const expiresAt = typeof account.expiresAt === "number" ? account.expiresAt : 0;
|
|
416
|
+
const lastRefresh = typeof account.lastRefresh === "string" ? new Date(account.lastRefresh).getTime() : 0;
|
|
417
|
+
const timeSinceRefresh = now - lastRefresh;
|
|
418
|
+
const timeToExpiry = expiresAt - now;
|
|
419
|
+
if (timeToExpiry <= 0) {
|
|
420
|
+
console.log(`[proactiveRefresh] ${alias}: token expired, needs reauth`);
|
|
421
|
+
continue;
|
|
422
|
+
}
|
|
423
|
+
if (timeSinceRefresh < REFRESH_COOLDOWN_MS) {
|
|
424
|
+
const hoursAgo = (timeSinceRefresh / (60 * 60 * 1000)).toFixed(1);
|
|
425
|
+
console.log(`[proactiveRefresh] ${alias}: refreshed ${hoursAgo}h ago, skipping`);
|
|
426
|
+
continue;
|
|
427
|
+
}
|
|
428
|
+
const hoursStale = (timeSinceRefresh / (60 * 60 * 1000)).toFixed(1);
|
|
429
|
+
console.log(`[proactiveRefresh] ${alias}: ${hoursStale}h since refresh, refreshing\u2026`);
|
|
430
|
+
const ok = await refreshSingleCodexToken(alias, account, storePath);
|
|
431
|
+
if (ok)
|
|
432
|
+
refreshed++;
|
|
433
|
+
}
|
|
434
|
+
if (refreshed > 0) {
|
|
435
|
+
console.log(`[proactiveRefresh] refreshed ${refreshed} codex token(s)`);
|
|
436
|
+
}
|
|
437
|
+
}
|
|
311
438
|
async function readCredentialFile(filename) {
|
|
312
439
|
const filePath = join8(homedir6(), ".config", "opencode", filename);
|
|
313
440
|
const text = await Bun.file(filePath).text();
|
|
@@ -415,8 +542,8 @@ async function clearStaleMetrics(provider, alias) {
|
|
|
415
542
|
const rl = acct.rateLimits;
|
|
416
543
|
for (const key of ["fiveHour", "weekly"]) {
|
|
417
544
|
const w2 = rl[key];
|
|
418
|
-
if (w2?.resetAt && typeof w2.resetAt === "
|
|
419
|
-
if (
|
|
545
|
+
if (w2?.resetAt && typeof w2.resetAt === "number") {
|
|
546
|
+
if (w2.resetAt < now) {
|
|
420
547
|
w2.remaining = w2.limit;
|
|
421
548
|
delete w2.resetAt;
|
|
422
549
|
changed = true;
|
|
@@ -461,7 +588,7 @@ function reauthCliCommand(provider) {
|
|
|
461
588
|
throw new Error(`Re-auth not supported for provider: ${provider}`);
|
|
462
589
|
return cmd;
|
|
463
590
|
}
|
|
464
|
-
var isBun4, PROVIDER_SOURCE, bunxQueue, REAUTH_PROVIDERS;
|
|
591
|
+
var isBun4, PROVIDER_SOURCE, CODEX_TOKEN_URL = "https://auth.openai.com/oauth/token", CODEX_CLIENT_ID = "app_EMoamEEZ73f0CkXaXp7hrann", REFRESH_COOLDOWN_MS, bunxQueue, REAUTH_PROVIDERS;
|
|
465
592
|
var init_plugin_adapters = __esm(() => {
|
|
466
593
|
init_command_runner();
|
|
467
594
|
init_config_service();
|
|
@@ -562,6 +689,7 @@ var init_plugin_adapters = __esm(() => {
|
|
|
562
689
|
return { ok: true };
|
|
563
690
|
}
|
|
564
691
|
});
|
|
692
|
+
REFRESH_COOLDOWN_MS = 24 * 60 * 60 * 1000;
|
|
565
693
|
bunxQueue = new Map;
|
|
566
694
|
registerCommand({
|
|
567
695
|
id: "accounts.ping",
|
|
@@ -602,6 +730,8 @@ var init_plugin_adapters = __esm(() => {
|
|
|
602
730
|
ctx.log("info", `Direct ping result: ${direct.status}`);
|
|
603
731
|
if (direct.status === "ok") {
|
|
604
732
|
await clearStaleMetrics(input.provider, input.alias);
|
|
733
|
+
proactiveRefreshCodexTokens().catch(() => {
|
|
734
|
+
});
|
|
605
735
|
return { status: "ok", message: "pong" };
|
|
606
736
|
}
|
|
607
737
|
if (direct.status === "expired") {
|
|
@@ -4972,10 +5102,10 @@ async function createCliRenderer(config = {}) {
|
|
|
4972
5102
|
await renderer.setupTerminal();
|
|
4973
5103
|
return renderer;
|
|
4974
5104
|
}
|
|
4975
|
-
var
|
|
4976
|
-
var
|
|
5105
|
+
var __defProp2 = Object.defineProperty;
|
|
5106
|
+
var __export2 = (target, all) => {
|
|
4977
5107
|
for (var name in all)
|
|
4978
|
-
|
|
5108
|
+
__defProp2(target, name, {
|
|
4979
5109
|
get: all[name],
|
|
4980
5110
|
enumerable: true,
|
|
4981
5111
|
configurable: true,
|
|
@@ -4984,7 +5114,7 @@ var __export = (target, all) => {
|
|
|
4984
5114
|
};
|
|
4985
5115
|
var __require = import.meta.require;
|
|
4986
5116
|
var exports_src = {};
|
|
4987
|
-
|
|
5117
|
+
__export2(exports_src, {
|
|
4988
5118
|
default: () => src_default,
|
|
4989
5119
|
Wrap: () => Wrap,
|
|
4990
5120
|
Unit: () => Unit,
|
|
@@ -30311,6 +30441,9 @@ function ensureActionsRegistered() {
|
|
|
30311
30441
|
var isBun5 = typeof globalThis.Bun !== "undefined";
|
|
30312
30442
|
var registered = false;
|
|
30313
30443
|
|
|
30444
|
+
// src/commander/server.ts
|
|
30445
|
+
init_plugin_adapters();
|
|
30446
|
+
|
|
30314
30447
|
// src/commander/services/app-init-service.ts
|
|
30315
30448
|
init_command_runner();
|
|
30316
30449
|
import { homedir as homedir7 } from "os";
|
|
@@ -30834,6 +30967,8 @@ async function runCommanderServer(args) {
|
|
|
30834
30967
|
});
|
|
30835
30968
|
const serverUrl = `http://${hostname}:${port}`;
|
|
30836
30969
|
console.log(`Commander ready at ${serverUrl}`);
|
|
30970
|
+
proactiveRefreshCodexTokens().catch(() => {
|
|
30971
|
+
});
|
|
30837
30972
|
if (isBun7 && !process.env.NO_OPEN) {
|
|
30838
30973
|
const cmd = process.platform === "win32" ? ["cmd", "/c", "start", serverUrl] : process.platform === "darwin" ? ["open", serverUrl] : ["xdg-open", serverUrl];
|
|
30839
30974
|
Bun.spawn(cmd, { stdio: ["ignore", "ignore", "ignore"] });
|
|
@@ -30979,4 +31114,4 @@ async function main2() {
|
|
|
30979
31114
|
var WATCH_INTERVAL_MS = 5 * 60 * 1000;
|
|
30980
31115
|
main2().catch(console.error);
|
|
30981
31116
|
|
|
30982
|
-
//# debugId=
|
|
31117
|
+
//# debugId=79D8BC2A04D06ACC64756E2164756E21
|