codexuse-cli 5.0.2 → 5.0.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/dist/index.js +449 -16
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2054,6 +2054,7 @@ var DEFAULT_AUTO_ROLL_ENABLED = false;
|
|
|
2054
2054
|
var DEFAULT_AUTO_ROLL_REARM_REMAINING_THRESHOLD = 15;
|
|
2055
2055
|
var DEFAULT_AUTO_ROLL_SWITCH_REMAINING_THRESHOLD = 5;
|
|
2056
2056
|
var DEFAULT_LAUNCH_OFFICIAL_CODEX_WHEN_CLOSED_ON_AUTO_ROLL = false;
|
|
2057
|
+
var DEFAULT_HANDOFF_RUNNING_THREADS_ON_ACCOUNT_SWITCH = false;
|
|
2057
2058
|
var DEFAULT_AUTO_ROLL_PRIORITY_ORDER = [];
|
|
2058
2059
|
var DEFAULT_LOW_REMAINING_NOTIFICATION_ENABLED = false;
|
|
2059
2060
|
var DEFAULT_LOW_REMAINING_NOTIFICATION_THRESHOLD = 1;
|
|
@@ -2157,6 +2158,7 @@ function normalizeAutoRollSettings(raw) {
|
|
|
2157
2158
|
rearmRemainingThreshold: normalizedRearm,
|
|
2158
2159
|
switchRemainingThreshold: normalizedSwitch,
|
|
2159
2160
|
launchOfficialCodexWhenClosedOnAutoRoll: raw?.launchOfficialCodexWhenClosedOnAutoRoll === true ? true : DEFAULT_LAUNCH_OFFICIAL_CODEX_WHEN_CLOSED_ON_AUTO_ROLL,
|
|
2161
|
+
handoffRunningThreadsOnAccountSwitch: raw?.handoffRunningThreadsOnAccountSwitch === true ? true : DEFAULT_HANDOFF_RUNNING_THREADS_ON_ACCOUNT_SWITCH,
|
|
2160
2162
|
priorityOrder: sanitizeAutoRollPriorityOrder(raw?.priorityOrder),
|
|
2161
2163
|
lowRemainingNotificationEnabled: raw?.lowRemainingNotificationEnabled === true ? true : DEFAULT_LOW_REMAINING_NOTIFICATION_ENABLED,
|
|
2162
2164
|
lowRemainingNotificationThreshold: sanitizeLowRemainingNotificationThreshold(
|
|
@@ -2530,6 +2532,7 @@ function createDefaultAppState() {
|
|
|
2530
2532
|
rearmRemainingThreshold: 15,
|
|
2531
2533
|
switchRemainingThreshold: 5,
|
|
2532
2534
|
launchOfficialCodexWhenClosedOnAutoRoll: false,
|
|
2535
|
+
handoffRunningThreadsOnAccountSwitch: false,
|
|
2533
2536
|
priorityOrder: [],
|
|
2534
2537
|
lowRemainingNotificationEnabled: false,
|
|
2535
2538
|
lowRemainingNotificationThreshold: 1
|
|
@@ -2631,6 +2634,20 @@ function asString(value) {
|
|
|
2631
2634
|
const trimmed = value.trim();
|
|
2632
2635
|
return trimmed.length > 0 ? trimmed : null;
|
|
2633
2636
|
}
|
|
2637
|
+
function asStringArray(value) {
|
|
2638
|
+
if (!Array.isArray(value)) {
|
|
2639
|
+
return [];
|
|
2640
|
+
}
|
|
2641
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2642
|
+
return value.flatMap((entry) => {
|
|
2643
|
+
const normalized = asString(entry);
|
|
2644
|
+
if (!normalized || seen.has(normalized)) {
|
|
2645
|
+
return [];
|
|
2646
|
+
}
|
|
2647
|
+
seen.add(normalized);
|
|
2648
|
+
return [normalized];
|
|
2649
|
+
});
|
|
2650
|
+
}
|
|
2634
2651
|
function asNumberOrNull(value) {
|
|
2635
2652
|
return typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
2636
2653
|
}
|
|
@@ -2954,7 +2971,10 @@ function normalizeAppState(raw) {
|
|
|
2954
2971
|
sourceProfileKey: asString(debt.sourceProfileKey),
|
|
2955
2972
|
decisionId: asString(debt.decisionId),
|
|
2956
2973
|
attempts: asNumberOrNull(debt.attempts) ?? 0,
|
|
2957
|
-
lastReason: asString(debt.lastReason)
|
|
2974
|
+
lastReason: asString(debt.lastReason),
|
|
2975
|
+
handoffReason: debt.handoffReason === "manual" || debt.handoffReason === "auto-roll" ? debt.handoffReason : null,
|
|
2976
|
+
handoffThreadIds: asStringArray(debt.handoffThreadIds),
|
|
2977
|
+
handoffSentThreadIds: asStringArray(debt.handoffSentThreadIds)
|
|
2958
2978
|
} : null;
|
|
2959
2979
|
} else {
|
|
2960
2980
|
merged.officialCodex.pendingActivationDebt = null;
|
|
@@ -3496,6 +3516,7 @@ async function writeCodexSettingsJsonRaw(payload) {
|
|
|
3496
3516
|
rearmRemainingThreshold: autoRoll.rearmRemainingThreshold,
|
|
3497
3517
|
switchRemainingThreshold: autoRoll.switchRemainingThreshold,
|
|
3498
3518
|
launchOfficialCodexWhenClosedOnAutoRoll: autoRoll.launchOfficialCodexWhenClosedOnAutoRoll,
|
|
3519
|
+
handoffRunningThreadsOnAccountSwitch: autoRoll.handoffRunningThreadsOnAccountSwitch,
|
|
3499
3520
|
priorityOrder: autoRoll.priorityOrder,
|
|
3500
3521
|
lowRemainingNotificationEnabled: autoRoll.lowRemainingNotificationEnabled,
|
|
3501
3522
|
lowRemainingNotificationThreshold: autoRoll.lowRemainingNotificationThreshold
|
|
@@ -3927,7 +3948,7 @@ var import_node_crypto3 = require("crypto");
|
|
|
3927
3948
|
var import_fs = require("fs");
|
|
3928
3949
|
var import_path = __toESM(require("path"), 1);
|
|
3929
3950
|
var import_path2 = require("path");
|
|
3930
|
-
var
|
|
3951
|
+
var import_toml2 = __toESM(require_toml(), 1);
|
|
3931
3952
|
|
|
3932
3953
|
// ../../packages/contracts/src/profiles/identity.ts
|
|
3933
3954
|
function normalizeValue(value) {
|
|
@@ -3981,6 +4002,7 @@ var import_node_readline = __toESM(require("readline"), 1);
|
|
|
3981
4002
|
var import_node_fs3 = require("fs");
|
|
3982
4003
|
var import_node_os2 = __toESM(require("os"), 1);
|
|
3983
4004
|
var import_node_path5 = __toESM(require("path"), 1);
|
|
4005
|
+
var import_toml = __toESM(require_toml(), 1);
|
|
3984
4006
|
|
|
3985
4007
|
// ../../packages/runtime-codex/src/codex/cli.ts
|
|
3986
4008
|
var import_node_fs2 = require("fs");
|
|
@@ -4206,6 +4228,12 @@ var ACTIVATION_MODEL = "gpt-5.1-codex-mini";
|
|
|
4206
4228
|
var MAX_STDERR_CAPTURE_CHARS = 32768;
|
|
4207
4229
|
var REFRESH_TOKEN_REDEEMED_SNIPPET = "refresh token was already used";
|
|
4208
4230
|
var authFileCache = /* @__PURE__ */ new Map();
|
|
4231
|
+
var CODEX_OAUTH_REFRESH_URL = "https://auth.openai.com/oauth/token";
|
|
4232
|
+
var CODEX_OAUTH_CLIENT_ID = "app_EMoamEEZ73f0CkXaXp7hrann";
|
|
4233
|
+
var CODEX_OAUTH_REFRESH_MAX_AGE_MS = 8 * 24 * 60 * 60 * 1e3;
|
|
4234
|
+
var CODEX_OAUTH_ACCESS_TOKEN_REFRESH_SKEW_MS = 2 * 60 * 1e3;
|
|
4235
|
+
var CODEX_USAGE_DEFAULT_BASE_URL = "https://chatgpt.com/backend-api";
|
|
4236
|
+
var CODEX_USAGE_TIMEOUT_MS = 3e4;
|
|
4209
4237
|
function isRecord4(value) {
|
|
4210
4238
|
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
4211
4239
|
}
|
|
@@ -4429,6 +4457,17 @@ function extractJwtIssuedAtMs(token) {
|
|
|
4429
4457
|
}
|
|
4430
4458
|
return issuedAt * 1e3;
|
|
4431
4459
|
}
|
|
4460
|
+
function extractJwtExpiresAtMs(token) {
|
|
4461
|
+
const payload = decodeJwtPayload(token);
|
|
4462
|
+
if (!payload) {
|
|
4463
|
+
return null;
|
|
4464
|
+
}
|
|
4465
|
+
const expiresAt = payload["exp"];
|
|
4466
|
+
if (typeof expiresAt !== "number" || !Number.isFinite(expiresAt)) {
|
|
4467
|
+
return null;
|
|
4468
|
+
}
|
|
4469
|
+
return expiresAt * 1e3;
|
|
4470
|
+
}
|
|
4432
4471
|
function parseAuthRecord(content) {
|
|
4433
4472
|
try {
|
|
4434
4473
|
const parsed = JSON.parse(content);
|
|
@@ -4517,6 +4556,353 @@ async function writeAuthFileCached(filePath, content) {
|
|
|
4517
4556
|
authFileCache.delete(filePath);
|
|
4518
4557
|
}
|
|
4519
4558
|
}
|
|
4559
|
+
function nonEmptyString(value) {
|
|
4560
|
+
return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
|
|
4561
|
+
}
|
|
4562
|
+
function resolveCodexAuthPathForEnv(envOverride) {
|
|
4563
|
+
return envOverride?.CODEX_HOME ? import_node_path5.default.join(envOverride.CODEX_HOME, "auth.json") : import_node_path5.default.join(
|
|
4564
|
+
envOverride?.HOME ?? process.env.HOME ?? import_node_os2.default.homedir(),
|
|
4565
|
+
".codex",
|
|
4566
|
+
"auth.json"
|
|
4567
|
+
);
|
|
4568
|
+
}
|
|
4569
|
+
function parseCodexOAuthCredentials(content) {
|
|
4570
|
+
const parsed = parseAuthRecord(content);
|
|
4571
|
+
if (!parsed) {
|
|
4572
|
+
return null;
|
|
4573
|
+
}
|
|
4574
|
+
const tokens = isRecord4(parsed.tokens) ? parsed.tokens : {};
|
|
4575
|
+
const accessToken = nonEmptyString(tokens.access_token) ?? nonEmptyString(parsed.access_token);
|
|
4576
|
+
const refreshToken = nonEmptyString(tokens.refresh_token) ?? nonEmptyString(parsed.refresh_token);
|
|
4577
|
+
if (!accessToken && !refreshToken) {
|
|
4578
|
+
return null;
|
|
4579
|
+
}
|
|
4580
|
+
return {
|
|
4581
|
+
accessToken: accessToken ?? "",
|
|
4582
|
+
refreshToken: refreshToken ?? "",
|
|
4583
|
+
idToken: nonEmptyString(tokens.id_token) ?? nonEmptyString(parsed.id_token),
|
|
4584
|
+
accountId: nonEmptyString(tokens.account_id) ?? nonEmptyString(parsed.account_id),
|
|
4585
|
+
lastRefreshMs: parseTimestamp2(tokens.last_refresh) ?? parseTimestamp2(parsed.last_refresh)
|
|
4586
|
+
};
|
|
4587
|
+
}
|
|
4588
|
+
function shouldRefreshCodexOAuthToken(credentials) {
|
|
4589
|
+
if (!credentials.refreshToken) {
|
|
4590
|
+
return false;
|
|
4591
|
+
}
|
|
4592
|
+
const accessExpiresAt = extractJwtExpiresAtMs(credentials.accessToken);
|
|
4593
|
+
if (accessExpiresAt !== null && accessExpiresAt - Date.now() <= CODEX_OAUTH_ACCESS_TOKEN_REFRESH_SKEW_MS) {
|
|
4594
|
+
return true;
|
|
4595
|
+
}
|
|
4596
|
+
if (credentials.lastRefreshMs === null) {
|
|
4597
|
+
return true;
|
|
4598
|
+
}
|
|
4599
|
+
return Date.now() - credentials.lastRefreshMs > CODEX_OAUTH_REFRESH_MAX_AGE_MS;
|
|
4600
|
+
}
|
|
4601
|
+
async function writeCodexOAuthCredentials(authPath, content, credentials) {
|
|
4602
|
+
let json = {};
|
|
4603
|
+
try {
|
|
4604
|
+
const parsed = JSON.parse(content);
|
|
4605
|
+
if (isRecord4(parsed)) {
|
|
4606
|
+
json = { ...parsed };
|
|
4607
|
+
}
|
|
4608
|
+
} catch {
|
|
4609
|
+
json = {};
|
|
4610
|
+
}
|
|
4611
|
+
const existingTokens = isRecord4(json.tokens) ? json.tokens : {};
|
|
4612
|
+
json.tokens = {
|
|
4613
|
+
...existingTokens,
|
|
4614
|
+
access_token: credentials.accessToken,
|
|
4615
|
+
refresh_token: credentials.refreshToken,
|
|
4616
|
+
...credentials.idToken ? { id_token: credentials.idToken } : {},
|
|
4617
|
+
...credentials.accountId ? { account_id: credentials.accountId } : {},
|
|
4618
|
+
last_refresh: (/* @__PURE__ */ new Date()).toISOString()
|
|
4619
|
+
};
|
|
4620
|
+
json.last_refresh = (/* @__PURE__ */ new Date()).toISOString();
|
|
4621
|
+
await writeAuthFileCached(authPath, `${JSON.stringify(json, null, 2)}
|
|
4622
|
+
`);
|
|
4623
|
+
}
|
|
4624
|
+
async function refreshCodexOAuthCredentials(credentials) {
|
|
4625
|
+
if (!credentials.refreshToken) {
|
|
4626
|
+
return credentials;
|
|
4627
|
+
}
|
|
4628
|
+
const response = await fetch(CODEX_OAUTH_REFRESH_URL, {
|
|
4629
|
+
method: "POST",
|
|
4630
|
+
headers: { "Content-Type": "application/json" },
|
|
4631
|
+
signal: AbortSignal.timeout(CODEX_USAGE_TIMEOUT_MS),
|
|
4632
|
+
body: JSON.stringify({
|
|
4633
|
+
client_id: CODEX_OAUTH_CLIENT_ID,
|
|
4634
|
+
grant_type: "refresh_token",
|
|
4635
|
+
refresh_token: credentials.refreshToken,
|
|
4636
|
+
scope: "openid profile email"
|
|
4637
|
+
})
|
|
4638
|
+
});
|
|
4639
|
+
const body = await response.text();
|
|
4640
|
+
if (!response.ok) {
|
|
4641
|
+
let code = "";
|
|
4642
|
+
try {
|
|
4643
|
+
const parsed2 = JSON.parse(body);
|
|
4644
|
+
if (isRecord4(parsed2)) {
|
|
4645
|
+
const error = isRecord4(parsed2.error) ? parsed2.error : {};
|
|
4646
|
+
code = nonEmptyString(error.code) ?? nonEmptyString(parsed2.error) ?? nonEmptyString(parsed2.code) ?? "";
|
|
4647
|
+
}
|
|
4648
|
+
} catch {
|
|
4649
|
+
code = "";
|
|
4650
|
+
}
|
|
4651
|
+
if (code === "refresh_token_reused") {
|
|
4652
|
+
throw new Error("refresh token was already used");
|
|
4653
|
+
}
|
|
4654
|
+
if (code === "refresh_token_expired") {
|
|
4655
|
+
throw new Error("refresh token expired");
|
|
4656
|
+
}
|
|
4657
|
+
if (code === "invalid_grant" || code === "refresh_token_invalidated") {
|
|
4658
|
+
throw new Error("refresh token was revoked");
|
|
4659
|
+
}
|
|
4660
|
+
throw new Error(`Codex OAuth refresh failed HTTP ${response.status}`);
|
|
4661
|
+
}
|
|
4662
|
+
let parsed;
|
|
4663
|
+
try {
|
|
4664
|
+
parsed = JSON.parse(body);
|
|
4665
|
+
} catch {
|
|
4666
|
+
throw new Error("Codex OAuth refresh returned invalid JSON");
|
|
4667
|
+
}
|
|
4668
|
+
if (!isRecord4(parsed)) {
|
|
4669
|
+
throw new Error("Codex OAuth refresh returned invalid data");
|
|
4670
|
+
}
|
|
4671
|
+
return {
|
|
4672
|
+
accessToken: nonEmptyString(parsed.access_token) ?? credentials.accessToken,
|
|
4673
|
+
refreshToken: nonEmptyString(parsed.refresh_token) ?? credentials.refreshToken,
|
|
4674
|
+
idToken: nonEmptyString(parsed.id_token) ?? credentials.idToken,
|
|
4675
|
+
accountId: credentials.accountId,
|
|
4676
|
+
lastRefreshMs: Date.now()
|
|
4677
|
+
};
|
|
4678
|
+
}
|
|
4679
|
+
async function loadCodexConfigContents(envOverride) {
|
|
4680
|
+
const codexHome = envOverride?.CODEX_HOME?.trim();
|
|
4681
|
+
const root = codexHome || import_node_path5.default.join(envOverride?.HOME ?? process.env.HOME ?? import_node_os2.default.homedir(), ".codex");
|
|
4682
|
+
try {
|
|
4683
|
+
return await import_node_fs3.promises.readFile(import_node_path5.default.join(root, "config.toml"), "utf8");
|
|
4684
|
+
} catch {
|
|
4685
|
+
return null;
|
|
4686
|
+
}
|
|
4687
|
+
}
|
|
4688
|
+
function parseChatGPTBaseUrl(contents) {
|
|
4689
|
+
if (!contents) {
|
|
4690
|
+
return CODEX_USAGE_DEFAULT_BASE_URL;
|
|
4691
|
+
}
|
|
4692
|
+
try {
|
|
4693
|
+
const parsed = (0, import_toml.parse)(contents);
|
|
4694
|
+
if (isRecord4(parsed)) {
|
|
4695
|
+
return nonEmptyString(parsed.chatgpt_base_url) ?? CODEX_USAGE_DEFAULT_BASE_URL;
|
|
4696
|
+
}
|
|
4697
|
+
} catch {
|
|
4698
|
+
return CODEX_USAGE_DEFAULT_BASE_URL;
|
|
4699
|
+
}
|
|
4700
|
+
return CODEX_USAGE_DEFAULT_BASE_URL;
|
|
4701
|
+
}
|
|
4702
|
+
async function resolveCodexUsageUrl(envOverride) {
|
|
4703
|
+
let base = parseChatGPTBaseUrl(await loadCodexConfigContents(envOverride));
|
|
4704
|
+
while (base.endsWith("/")) {
|
|
4705
|
+
base = base.slice(0, -1);
|
|
4706
|
+
}
|
|
4707
|
+
if ((base.startsWith("https://chatgpt.com") || base.startsWith("https://chat.openai.com")) && !base.includes("/backend-api")) {
|
|
4708
|
+
base += "/backend-api";
|
|
4709
|
+
}
|
|
4710
|
+
return `${base}${base.includes("/backend-api") ? "/wham/usage" : "/api/codex/usage"}`;
|
|
4711
|
+
}
|
|
4712
|
+
function numberFromUnknown(value) {
|
|
4713
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
4714
|
+
return value;
|
|
4715
|
+
}
|
|
4716
|
+
if (typeof value === "string" && value.trim()) {
|
|
4717
|
+
const parsed = Number(value.trim());
|
|
4718
|
+
return Number.isFinite(parsed) ? parsed : null;
|
|
4719
|
+
}
|
|
4720
|
+
return null;
|
|
4721
|
+
}
|
|
4722
|
+
function toOAuthWindow(window) {
|
|
4723
|
+
if (!window) {
|
|
4724
|
+
return void 0;
|
|
4725
|
+
}
|
|
4726
|
+
const usedPercent = numberFromUnknown(window.used_percent);
|
|
4727
|
+
const resetAt = numberFromUnknown(window.reset_at);
|
|
4728
|
+
const limitWindowSeconds = numberFromUnknown(window.limit_window_seconds);
|
|
4729
|
+
if (usedPercent === null && resetAt === null && limitWindowSeconds === null) {
|
|
4730
|
+
return void 0;
|
|
4731
|
+
}
|
|
4732
|
+
const result = {};
|
|
4733
|
+
if (usedPercent !== null) {
|
|
4734
|
+
result.usedPercent = Math.max(0, Math.min(100, usedPercent));
|
|
4735
|
+
}
|
|
4736
|
+
if (limitWindowSeconds !== null) {
|
|
4737
|
+
result.windowMinutes = Math.round(limitWindowSeconds / 60);
|
|
4738
|
+
}
|
|
4739
|
+
if (resetAt !== null) {
|
|
4740
|
+
const resetMs = resetAt > 1e10 ? resetAt : resetAt * 1e3;
|
|
4741
|
+
result.resetsAt = new Date(resetMs).toISOString();
|
|
4742
|
+
result.resetsInSeconds = Math.max(0, Math.round((resetMs - Date.now()) / 1e3));
|
|
4743
|
+
}
|
|
4744
|
+
return result;
|
|
4745
|
+
}
|
|
4746
|
+
function firstNonEmptyString(...values) {
|
|
4747
|
+
for (const value of values) {
|
|
4748
|
+
const text = nonEmptyString(value);
|
|
4749
|
+
if (text) {
|
|
4750
|
+
return text;
|
|
4751
|
+
}
|
|
4752
|
+
}
|
|
4753
|
+
return null;
|
|
4754
|
+
}
|
|
4755
|
+
function slugForRateLimitId(value) {
|
|
4756
|
+
return value.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
4757
|
+
}
|
|
4758
|
+
function isSparkRateLimit(limitName, meteredFeature) {
|
|
4759
|
+
return [limitName, meteredFeature].filter((value) => Boolean(value)).some((value) => value.toLowerCase().includes("spark"));
|
|
4760
|
+
}
|
|
4761
|
+
function sparkWindowIdentity(window, fallback) {
|
|
4762
|
+
const limitWindowSeconds = numberFromUnknown(window.limit_window_seconds);
|
|
4763
|
+
const windowMinutes = limitWindowSeconds !== null ? Math.round(limitWindowSeconds / 60) : null;
|
|
4764
|
+
const kind = windowMinutes !== null && windowMinutes > 0 && windowMinutes <= 6 * 60 ? "five-hour" : windowMinutes !== null && windowMinutes >= 6 * 24 * 60 ? "weekly" : fallback;
|
|
4765
|
+
return kind === "weekly" ? { id: "codex-spark-weekly", label: "Codex Spark Weekly" } : { id: "codex-spark", label: "Codex Spark 5-hour" };
|
|
4766
|
+
}
|
|
4767
|
+
function toExtraRateLimitWindow(args) {
|
|
4768
|
+
const window = toOAuthWindow(args.window);
|
|
4769
|
+
if (!window) {
|
|
4770
|
+
return null;
|
|
4771
|
+
}
|
|
4772
|
+
return {
|
|
4773
|
+
id: args.id,
|
|
4774
|
+
label: args.label,
|
|
4775
|
+
...args.meteredFeature ? { meteredFeature: args.meteredFeature } : {},
|
|
4776
|
+
...window
|
|
4777
|
+
};
|
|
4778
|
+
}
|
|
4779
|
+
function parseExtraRateLimitWindows(additionalRateLimits) {
|
|
4780
|
+
if (!Array.isArray(additionalRateLimits)) {
|
|
4781
|
+
return [];
|
|
4782
|
+
}
|
|
4783
|
+
const usedIds = /* @__PURE__ */ new Set();
|
|
4784
|
+
const extraWindows = [];
|
|
4785
|
+
for (const entry of additionalRateLimits) {
|
|
4786
|
+
if (!isRecord4(entry)) {
|
|
4787
|
+
continue;
|
|
4788
|
+
}
|
|
4789
|
+
const limitName = firstNonEmptyString(entry.limit_name);
|
|
4790
|
+
const meteredFeature = firstNonEmptyString(entry.metered_feature);
|
|
4791
|
+
const rateLimit = isRecord4(entry.rate_limit) ? entry.rate_limit : null;
|
|
4792
|
+
if (!rateLimit) {
|
|
4793
|
+
continue;
|
|
4794
|
+
}
|
|
4795
|
+
const primary = isRecord4(rateLimit.primary_window) ? rateLimit.primary_window : null;
|
|
4796
|
+
const secondary = isRecord4(rateLimit.secondary_window) ? rateLimit.secondary_window : null;
|
|
4797
|
+
if (isSparkRateLimit(limitName, meteredFeature)) {
|
|
4798
|
+
const candidates = [
|
|
4799
|
+
{ window: primary, fallback: "five-hour" },
|
|
4800
|
+
{ window: secondary, fallback: "weekly" }
|
|
4801
|
+
];
|
|
4802
|
+
for (const candidate of candidates) {
|
|
4803
|
+
if (!candidate.window) {
|
|
4804
|
+
continue;
|
|
4805
|
+
}
|
|
4806
|
+
const identity = sparkWindowIdentity(candidate.window, candidate.fallback);
|
|
4807
|
+
if (usedIds.has(identity.id)) {
|
|
4808
|
+
continue;
|
|
4809
|
+
}
|
|
4810
|
+
const extraWindow2 = toExtraRateLimitWindow({
|
|
4811
|
+
...identity,
|
|
4812
|
+
meteredFeature,
|
|
4813
|
+
window: candidate.window
|
|
4814
|
+
});
|
|
4815
|
+
if (extraWindow2) {
|
|
4816
|
+
usedIds.add(identity.id);
|
|
4817
|
+
extraWindows.push(extraWindow2);
|
|
4818
|
+
}
|
|
4819
|
+
}
|
|
4820
|
+
continue;
|
|
4821
|
+
}
|
|
4822
|
+
const idSource = firstNonEmptyString(meteredFeature, limitName);
|
|
4823
|
+
const slug = idSource ? slugForRateLimitId(idSource) : "";
|
|
4824
|
+
if (!slug) {
|
|
4825
|
+
continue;
|
|
4826
|
+
}
|
|
4827
|
+
const id = `codex-${slug}`;
|
|
4828
|
+
if (usedIds.has(id)) {
|
|
4829
|
+
continue;
|
|
4830
|
+
}
|
|
4831
|
+
const extraWindow = toExtraRateLimitWindow({
|
|
4832
|
+
id,
|
|
4833
|
+
label: firstNonEmptyString(limitName, meteredFeature) ?? "Codex extra limit",
|
|
4834
|
+
meteredFeature,
|
|
4835
|
+
window: primary ?? secondary
|
|
4836
|
+
});
|
|
4837
|
+
if (extraWindow) {
|
|
4838
|
+
usedIds.add(id);
|
|
4839
|
+
extraWindows.push(extraWindow);
|
|
4840
|
+
}
|
|
4841
|
+
}
|
|
4842
|
+
return extraWindows;
|
|
4843
|
+
}
|
|
4844
|
+
function parseRateLimitSnapshotFromOAuthUsage(value) {
|
|
4845
|
+
if (!isRecord4(value)) {
|
|
4846
|
+
throw new Error("Codex usage API returned invalid data");
|
|
4847
|
+
}
|
|
4848
|
+
const usage = value;
|
|
4849
|
+
const primary = toOAuthWindow(usage.rate_limit?.primary_window ?? null);
|
|
4850
|
+
const secondary = toOAuthWindow(usage.rate_limit?.secondary_window ?? null);
|
|
4851
|
+
const extraWindows = parseExtraRateLimitWindows(usage.additional_rate_limits);
|
|
4852
|
+
if (!primary && !secondary && extraWindows.length === 0) {
|
|
4853
|
+
return null;
|
|
4854
|
+
}
|
|
4855
|
+
return {
|
|
4856
|
+
lastUpdated: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4857
|
+
source: "codex-oauth",
|
|
4858
|
+
...primary ? { primary } : {},
|
|
4859
|
+
...secondary ? { secondary } : {},
|
|
4860
|
+
...extraWindows.length > 0 ? { extraWindows } : {}
|
|
4861
|
+
};
|
|
4862
|
+
}
|
|
4863
|
+
async function fetchRateLimitsViaOAuth(envOverride) {
|
|
4864
|
+
const authPath = resolveCodexAuthPathForEnv(envOverride);
|
|
4865
|
+
const authContent = await readAuthFileCached(authPath);
|
|
4866
|
+
if (!authContent) {
|
|
4867
|
+
return null;
|
|
4868
|
+
}
|
|
4869
|
+
let credentials = parseCodexOAuthCredentials(authContent);
|
|
4870
|
+
if (!credentials) {
|
|
4871
|
+
return null;
|
|
4872
|
+
}
|
|
4873
|
+
if (shouldRefreshCodexOAuthToken(credentials)) {
|
|
4874
|
+
credentials = await refreshCodexOAuthCredentials(credentials);
|
|
4875
|
+
await writeCodexOAuthCredentials(authPath, authContent, credentials);
|
|
4876
|
+
}
|
|
4877
|
+
if (!credentials.accessToken) {
|
|
4878
|
+
return null;
|
|
4879
|
+
}
|
|
4880
|
+
const usageUrl = await resolveCodexUsageUrl(envOverride);
|
|
4881
|
+
const response = await fetch(usageUrl, {
|
|
4882
|
+
method: "GET",
|
|
4883
|
+
headers: {
|
|
4884
|
+
Authorization: `Bearer ${credentials.accessToken}`,
|
|
4885
|
+
Accept: "application/json",
|
|
4886
|
+
"User-Agent": "CodexUse",
|
|
4887
|
+
...credentials.accountId ? { "ChatGPT-Account-Id": credentials.accountId } : {}
|
|
4888
|
+
},
|
|
4889
|
+
signal: AbortSignal.timeout(CODEX_USAGE_TIMEOUT_MS)
|
|
4890
|
+
});
|
|
4891
|
+
const body = await response.text();
|
|
4892
|
+
if (response.status === 401 || response.status === 403) {
|
|
4893
|
+
throw new Error(`Codex OAuth usage failed HTTP ${response.status} (${usageUrl})`);
|
|
4894
|
+
}
|
|
4895
|
+
if (!response.ok) {
|
|
4896
|
+
throw new Error(`Codex usage API failed HTTP ${response.status} (${usageUrl}): ${body.slice(0, 500)}`);
|
|
4897
|
+
}
|
|
4898
|
+
let parsed;
|
|
4899
|
+
try {
|
|
4900
|
+
parsed = JSON.parse(body);
|
|
4901
|
+
} catch {
|
|
4902
|
+
throw new Error("Codex usage API returned invalid JSON");
|
|
4903
|
+
}
|
|
4904
|
+
return parseRateLimitSnapshotFromOAuthUsage(parsed);
|
|
4905
|
+
}
|
|
4520
4906
|
function inferRefreshFailureHint(stderrOutput) {
|
|
4521
4907
|
if (!stderrOutput) {
|
|
4522
4908
|
return null;
|
|
@@ -6129,7 +6515,7 @@ var ProfileManager = class {
|
|
|
6129
6515
|
}
|
|
6130
6516
|
let parsed;
|
|
6131
6517
|
try {
|
|
6132
|
-
parsed = (0,
|
|
6518
|
+
parsed = (0, import_toml2.parse)(raw);
|
|
6133
6519
|
} catch (error) {
|
|
6134
6520
|
logWarn(`Failed to parse config.toml for profile home '${profileHome}':`, error);
|
|
6135
6521
|
return;
|
|
@@ -6139,7 +6525,7 @@ var ProfileManager = class {
|
|
|
6139
6525
|
}
|
|
6140
6526
|
delete parsed["model_reasoning_effort"];
|
|
6141
6527
|
const sanitized = this.ensureTrailingNewline(
|
|
6142
|
-
(0,
|
|
6528
|
+
(0, import_toml2.stringify)(parsed)
|
|
6143
6529
|
);
|
|
6144
6530
|
try {
|
|
6145
6531
|
await import_fs.promises.writeFile(configPath, sanitized, "utf8");
|
|
@@ -6503,7 +6889,16 @@ var ProfileManager = class {
|
|
|
6503
6889
|
profileName,
|
|
6504
6890
|
() => this.runWithPreparedProfileHome(
|
|
6505
6891
|
profileName,
|
|
6506
|
-
(env) =>
|
|
6892
|
+
async (env) => {
|
|
6893
|
+
try {
|
|
6894
|
+
const oauthSnapshot = await fetchRateLimitsViaOAuth(env);
|
|
6895
|
+
if (oauthSnapshot) {
|
|
6896
|
+
return oauthSnapshot;
|
|
6897
|
+
}
|
|
6898
|
+
} catch {
|
|
6899
|
+
}
|
|
6900
|
+
return fetchRateLimitsViaRpc(env, { codexPath: options.codexPath });
|
|
6901
|
+
},
|
|
6507
6902
|
{ syncFromActiveAuthBeforeAction: false }
|
|
6508
6903
|
)
|
|
6509
6904
|
);
|
|
@@ -7877,7 +8272,8 @@ function maxUsedPercent(snapshot) {
|
|
|
7877
8272
|
}
|
|
7878
8273
|
const candidates = [
|
|
7879
8274
|
snapshot.primary?.usedPercent,
|
|
7880
|
-
snapshot.secondary?.usedPercent
|
|
8275
|
+
snapshot.secondary?.usedPercent,
|
|
8276
|
+
...(snapshot.extraWindows ?? []).map((window) => window.usedPercent)
|
|
7881
8277
|
].filter((value) => typeof value === "number" && Number.isFinite(value));
|
|
7882
8278
|
if (candidates.length === 0) {
|
|
7883
8279
|
return null;
|
|
@@ -8006,7 +8402,8 @@ function formatUsagePercent(value) {
|
|
|
8006
8402
|
function snapshotResetSeconds(snapshot) {
|
|
8007
8403
|
const values = [
|
|
8008
8404
|
snapshot?.primary?.resetsInSeconds,
|
|
8009
|
-
snapshot?.secondary?.resetsInSeconds
|
|
8405
|
+
snapshot?.secondary?.resetsInSeconds,
|
|
8406
|
+
...(snapshot?.extraWindows ?? []).map((window) => window.resetsInSeconds)
|
|
8010
8407
|
].filter((value) => typeof value === "number" && Number.isFinite(value));
|
|
8011
8408
|
return values.length > 0 ? Math.min(...values) : null;
|
|
8012
8409
|
}
|
|
@@ -12743,9 +13140,12 @@ function isMainProcessRow(row, candidate) {
|
|
|
12743
13140
|
}
|
|
12744
13141
|
function findAppServerPid(rows, mainPid) {
|
|
12745
13142
|
return rows.find(
|
|
12746
|
-
(row) => row.ppid === mainPid && row
|
|
13143
|
+
(row) => row.ppid === mainPid && isAppServerProcessRow(row)
|
|
12747
13144
|
)?.pid ?? null;
|
|
12748
13145
|
}
|
|
13146
|
+
function isAppServerProcessRow(row) {
|
|
13147
|
+
return row.args.includes("/Contents/Resources/codex app-server");
|
|
13148
|
+
}
|
|
12749
13149
|
async function processMatchesProfileHome(pid, profileHome) {
|
|
12750
13150
|
const result = await runCommand2(
|
|
12751
13151
|
"/bin/ps",
|
|
@@ -12759,7 +13159,7 @@ async function processMatchesProfileHome(pid, profileHome) {
|
|
|
12759
13159
|
}
|
|
12760
13160
|
async function findAppServerPidForProfile(rows, mainPid, profileHome) {
|
|
12761
13161
|
const candidates = rows.filter(
|
|
12762
|
-
(row) => row.ppid === mainPid && row
|
|
13162
|
+
(row) => row.ppid === mainPid && isAppServerProcessRow(row)
|
|
12763
13163
|
);
|
|
12764
13164
|
for (const row of candidates) {
|
|
12765
13165
|
if (row.args.includes(profileHome) || await processMatchesProfileHome(row.pid, profileHome)) {
|
|
@@ -12768,6 +13168,19 @@ async function findAppServerPidForProfile(rows, mainPid, profileHome) {
|
|
|
12768
13168
|
}
|
|
12769
13169
|
return null;
|
|
12770
13170
|
}
|
|
13171
|
+
async function resolveStoredAppServerPid(instance, rows) {
|
|
13172
|
+
if (!instance.appServerPid) {
|
|
13173
|
+
return null;
|
|
13174
|
+
}
|
|
13175
|
+
const row = rows.find((entry) => entry.pid === instance.appServerPid);
|
|
13176
|
+
if (!row || !isAppServerProcessRow(row)) {
|
|
13177
|
+
return null;
|
|
13178
|
+
}
|
|
13179
|
+
if (instance.profileHome && !row.args.includes(instance.profileHome) && !await processMatchesProfileHome(row.pid, instance.profileHome)) {
|
|
13180
|
+
return null;
|
|
13181
|
+
}
|
|
13182
|
+
return row.pid;
|
|
13183
|
+
}
|
|
12771
13184
|
function parseRemoteDebuggingPort(args) {
|
|
12772
13185
|
const match = args.match(/--remote-debugging-port(?:=|\s+)(\d{2,5})\b/);
|
|
12773
13186
|
const port = Number(match?.[1]);
|
|
@@ -12896,11 +13309,27 @@ async function readManagedInstance(profileName) {
|
|
|
12896
13309
|
async function resolveInstanceRuntimeState(args) {
|
|
12897
13310
|
const pid = args.instance.pid;
|
|
12898
13311
|
if (!pid) {
|
|
12899
|
-
|
|
13312
|
+
const appServerPid2 = await resolveStoredAppServerPid(
|
|
13313
|
+
args.instance,
|
|
13314
|
+
args.rows
|
|
13315
|
+
);
|
|
13316
|
+
return {
|
|
13317
|
+
running: appServerPid2 !== null,
|
|
13318
|
+
appServerPid: appServerPid2,
|
|
13319
|
+
startedAt: null
|
|
13320
|
+
};
|
|
12900
13321
|
}
|
|
12901
13322
|
const mainRow = args.rows.find((row) => row.pid === pid);
|
|
12902
13323
|
if (!mainRow || !isMainProcessRow(mainRow, args.candidate)) {
|
|
12903
|
-
|
|
13324
|
+
const appServerPid2 = await resolveStoredAppServerPid(
|
|
13325
|
+
args.instance,
|
|
13326
|
+
args.rows
|
|
13327
|
+
);
|
|
13328
|
+
return {
|
|
13329
|
+
running: appServerPid2 !== null,
|
|
13330
|
+
appServerPid: appServerPid2,
|
|
13331
|
+
startedAt: null
|
|
13332
|
+
};
|
|
12904
13333
|
}
|
|
12905
13334
|
const appServerPid = args.instance.profileHome ? await findAppServerPidForProfile(args.rows, pid, args.instance.profileHome) : findAppServerPid(args.rows, pid);
|
|
12906
13335
|
if (args.instance.profileHome && appServerPid === null) {
|
|
@@ -13813,8 +14242,8 @@ function formatResetText(window) {
|
|
|
13813
14242
|
if (durationText === "now") return "now";
|
|
13814
14243
|
return `in ${durationText}`;
|
|
13815
14244
|
}
|
|
13816
|
-
function formatWindowUsage(window) {
|
|
13817
|
-
const label = formatWindowLabel(window.windowMinutes) ?? "window";
|
|
14245
|
+
function formatWindowUsage(window, labelOverride) {
|
|
14246
|
+
const label = labelOverride ?? formatWindowLabel(window.windowMinutes) ?? "window";
|
|
13818
14247
|
const usedPercent = typeof window.usedPercent === "number" && Number.isFinite(window.usedPercent) ? `${Math.round(Math.max(0, Math.min(100, window.usedPercent)))}%` : "n/a";
|
|
13819
14248
|
const resetText = formatResetText(window);
|
|
13820
14249
|
return {
|
|
@@ -13943,6 +14372,10 @@ async function resolveProfileUsage(manager, profile, codexPath) {
|
|
|
13943
14372
|
const row = formatWindowUsage(snapshot.secondary);
|
|
13944
14373
|
if (row) rows.push(row);
|
|
13945
14374
|
}
|
|
14375
|
+
for (const window of snapshot.extraWindows ?? []) {
|
|
14376
|
+
const row = formatWindowUsage(window, window.label);
|
|
14377
|
+
if (row) rows.push(row);
|
|
14378
|
+
}
|
|
13946
14379
|
const maxPercent = maxUsedPercent(snapshot);
|
|
13947
14380
|
const summary = typeof maxPercent === "number" ? `${Math.round(maxPercent)}%` : "n/a";
|
|
13948
14381
|
return { summary, rows: rows.length > 0 ? rows : null, usageSummary: summarizeRateLimitSnapshot(snapshot) };
|
|
@@ -14833,7 +15266,7 @@ async function pushRemoteSnapshot(licenseKey, snapshot, options) {
|
|
|
14833
15266
|
}
|
|
14834
15267
|
|
|
14835
15268
|
// ../../packages/runtime-codex/src/codex/config.ts
|
|
14836
|
-
var
|
|
15269
|
+
var import_toml3 = __toESM(require_toml(), 1);
|
|
14837
15270
|
|
|
14838
15271
|
// ../../packages/runtime-codex/src/codex/config-metadata.ts
|
|
14839
15272
|
var import_node_child_process6 = require("child_process");
|
|
@@ -15472,7 +15905,7 @@ function formatUserFacingError(error, options = {}) {
|
|
|
15472
15905
|
// ../../packages/runtime-codex/src/codex/config.ts
|
|
15473
15906
|
function tryParseToml(content) {
|
|
15474
15907
|
try {
|
|
15475
|
-
const parsed = (0,
|
|
15908
|
+
const parsed = (0, import_toml3.parse)(content);
|
|
15476
15909
|
return { data: parsed ?? {}, error: null };
|
|
15477
15910
|
} catch (error) {
|
|
15478
15911
|
const maybe = error;
|
|
@@ -16157,7 +16590,7 @@ async function ensureCliStorageReady() {
|
|
|
16157
16590
|
}
|
|
16158
16591
|
|
|
16159
16592
|
// src/app/main.ts
|
|
16160
|
-
var VERSION = true ? "5.0.
|
|
16593
|
+
var VERSION = true ? "5.0.6" : "0.0.0";
|
|
16161
16594
|
async function runCli() {
|
|
16162
16595
|
const args = process.argv.slice(2);
|
|
16163
16596
|
if (args.length === 0) {
|