opencode-usage 0.5.4 → 0.5.5
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,115 @@ 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, store) {
|
|
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
|
+
account.accessToken = tokens.access_token;
|
|
370
|
+
if (tokens.refresh_token)
|
|
371
|
+
account.refreshToken = tokens.refresh_token;
|
|
372
|
+
if (tokens.id_token)
|
|
373
|
+
account.idToken = tokens.id_token;
|
|
374
|
+
account.expiresAt = expiresAt;
|
|
375
|
+
account.lastRefresh = new Date().toISOString();
|
|
376
|
+
account.accountId = getAccountIdFromJwt(idClaims) ?? getAccountIdFromJwt(accessClaims) ?? account.accountId;
|
|
377
|
+
account.authInvalid = false;
|
|
378
|
+
await Bun.write(storePath, JSON.stringify(store, null, 2));
|
|
379
|
+
const daysLeft = ((expiresAt - Date.now()) / (24 * 60 * 60 * 1000)).toFixed(1);
|
|
380
|
+
console.log(`[proactiveRefresh] ${alias}: refreshed in ${Date.now() - t0}ms, new expiry in ${daysLeft}d`);
|
|
381
|
+
return true;
|
|
382
|
+
} catch (err) {
|
|
383
|
+
console.log(`[proactiveRefresh] ${alias}: error \u2014 ${err instanceof Error ? err.message : String(err)}`);
|
|
384
|
+
return false;
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
async function proactiveRefreshCodexTokens() {
|
|
388
|
+
const STORE_PATHS = [
|
|
389
|
+
join8(homedir6(), ".config", "opencode", "codex-multi-account-accounts.json"),
|
|
390
|
+
join8(homedir6(), ".config", "opencode", "codex-multi-accounts.json"),
|
|
391
|
+
join8(homedir6(), ".config", "oc-codex-multi-account", "accounts.json")
|
|
392
|
+
];
|
|
393
|
+
let storePath = null;
|
|
394
|
+
let store = null;
|
|
395
|
+
for (const p of STORE_PATHS) {
|
|
396
|
+
try {
|
|
397
|
+
store = JSON.parse(await Bun.file(p).text());
|
|
398
|
+
storePath = p;
|
|
399
|
+
break;
|
|
400
|
+
} catch {
|
|
401
|
+
continue;
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
if (!store || !storePath)
|
|
405
|
+
return;
|
|
406
|
+
const accounts = store.accounts ?? {};
|
|
407
|
+
const now = Date.now();
|
|
408
|
+
let refreshed = 0;
|
|
409
|
+
for (const [alias, account] of Object.entries(accounts)) {
|
|
410
|
+
const expiresAt = typeof account.expiresAt === "number" ? account.expiresAt : 0;
|
|
411
|
+
const lastRefresh = typeof account.lastRefresh === "string" ? new Date(account.lastRefresh).getTime() : 0;
|
|
412
|
+
const timeSinceRefresh = now - lastRefresh;
|
|
413
|
+
const timeToExpiry = expiresAt - now;
|
|
414
|
+
if (timeToExpiry <= 0) {
|
|
415
|
+
console.log(`[proactiveRefresh] ${alias}: token expired, needs reauth`);
|
|
416
|
+
continue;
|
|
417
|
+
}
|
|
418
|
+
if (timeSinceRefresh < REFRESH_COOLDOWN_MS) {
|
|
419
|
+
const hoursAgo = (timeSinceRefresh / (60 * 60 * 1000)).toFixed(1);
|
|
420
|
+
console.log(`[proactiveRefresh] ${alias}: refreshed ${hoursAgo}h ago, skipping`);
|
|
421
|
+
continue;
|
|
422
|
+
}
|
|
423
|
+
const hoursStale = (timeSinceRefresh / (60 * 60 * 1000)).toFixed(1);
|
|
424
|
+
console.log(`[proactiveRefresh] ${alias}: ${hoursStale}h since refresh, refreshing\u2026`);
|
|
425
|
+
const ok = await refreshSingleCodexToken(alias, account, storePath, store);
|
|
426
|
+
if (ok)
|
|
427
|
+
refreshed++;
|
|
428
|
+
}
|
|
429
|
+
if (refreshed > 0) {
|
|
430
|
+
console.log(`[proactiveRefresh] refreshed ${refreshed} codex token(s)`);
|
|
431
|
+
}
|
|
432
|
+
}
|
|
311
433
|
async function readCredentialFile(filename) {
|
|
312
434
|
const filePath = join8(homedir6(), ".config", "opencode", filename);
|
|
313
435
|
const text = await Bun.file(filePath).text();
|
|
@@ -461,7 +583,7 @@ function reauthCliCommand(provider) {
|
|
|
461
583
|
throw new Error(`Re-auth not supported for provider: ${provider}`);
|
|
462
584
|
return cmd;
|
|
463
585
|
}
|
|
464
|
-
var isBun4, PROVIDER_SOURCE, bunxQueue, REAUTH_PROVIDERS;
|
|
586
|
+
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
587
|
var init_plugin_adapters = __esm(() => {
|
|
466
588
|
init_command_runner();
|
|
467
589
|
init_config_service();
|
|
@@ -562,6 +684,7 @@ var init_plugin_adapters = __esm(() => {
|
|
|
562
684
|
return { ok: true };
|
|
563
685
|
}
|
|
564
686
|
});
|
|
687
|
+
REFRESH_COOLDOWN_MS = 24 * 60 * 60 * 1000;
|
|
565
688
|
bunxQueue = new Map;
|
|
566
689
|
registerCommand({
|
|
567
690
|
id: "accounts.ping",
|
|
@@ -602,6 +725,8 @@ var init_plugin_adapters = __esm(() => {
|
|
|
602
725
|
ctx.log("info", `Direct ping result: ${direct.status}`);
|
|
603
726
|
if (direct.status === "ok") {
|
|
604
727
|
await clearStaleMetrics(input.provider, input.alias);
|
|
728
|
+
proactiveRefreshCodexTokens().catch(() => {
|
|
729
|
+
});
|
|
605
730
|
return { status: "ok", message: "pong" };
|
|
606
731
|
}
|
|
607
732
|
if (direct.status === "expired") {
|
|
@@ -4972,10 +5097,10 @@ async function createCliRenderer(config = {}) {
|
|
|
4972
5097
|
await renderer.setupTerminal();
|
|
4973
5098
|
return renderer;
|
|
4974
5099
|
}
|
|
4975
|
-
var
|
|
4976
|
-
var
|
|
5100
|
+
var __defProp2 = Object.defineProperty;
|
|
5101
|
+
var __export2 = (target, all) => {
|
|
4977
5102
|
for (var name in all)
|
|
4978
|
-
|
|
5103
|
+
__defProp2(target, name, {
|
|
4979
5104
|
get: all[name],
|
|
4980
5105
|
enumerable: true,
|
|
4981
5106
|
configurable: true,
|
|
@@ -4984,7 +5109,7 @@ var __export = (target, all) => {
|
|
|
4984
5109
|
};
|
|
4985
5110
|
var __require = import.meta.require;
|
|
4986
5111
|
var exports_src = {};
|
|
4987
|
-
|
|
5112
|
+
__export2(exports_src, {
|
|
4988
5113
|
default: () => src_default,
|
|
4989
5114
|
Wrap: () => Wrap,
|
|
4990
5115
|
Unit: () => Unit,
|
|
@@ -30311,6 +30436,9 @@ function ensureActionsRegistered() {
|
|
|
30311
30436
|
var isBun5 = typeof globalThis.Bun !== "undefined";
|
|
30312
30437
|
var registered = false;
|
|
30313
30438
|
|
|
30439
|
+
// src/commander/server.ts
|
|
30440
|
+
init_plugin_adapters();
|
|
30441
|
+
|
|
30314
30442
|
// src/commander/services/app-init-service.ts
|
|
30315
30443
|
init_command_runner();
|
|
30316
30444
|
import { homedir as homedir7 } from "os";
|
|
@@ -30834,6 +30962,8 @@ async function runCommanderServer(args) {
|
|
|
30834
30962
|
});
|
|
30835
30963
|
const serverUrl = `http://${hostname}:${port}`;
|
|
30836
30964
|
console.log(`Commander ready at ${serverUrl}`);
|
|
30965
|
+
proactiveRefreshCodexTokens().catch(() => {
|
|
30966
|
+
});
|
|
30837
30967
|
if (isBun7 && !process.env.NO_OPEN) {
|
|
30838
30968
|
const cmd = process.platform === "win32" ? ["cmd", "/c", "start", serverUrl] : process.platform === "darwin" ? ["open", serverUrl] : ["xdg-open", serverUrl];
|
|
30839
30969
|
Bun.spawn(cmd, { stdio: ["ignore", "ignore", "ignore"] });
|
|
@@ -30979,4 +31109,4 @@ async function main2() {
|
|
|
30979
31109
|
var WATCH_INTERVAL_MS = 5 * 60 * 1000;
|
|
30980
31110
|
main2().catch(console.error);
|
|
30981
31111
|
|
|
30982
|
-
//# debugId=
|
|
31112
|
+
//# debugId=DF21C38AB467E5FE64756E2164756E21
|