@bman654/clodex 2.10.0 → 2.11.1
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 +19 -15
- package/dist/cli.js +83 -22
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -241,11 +241,13 @@ Manage favorite models (max 20) and short aliases. Favorites feed the endpoint-m
|
|
|
241
241
|
|
|
242
242
|
#### Context stops and the pricing boundary
|
|
243
243
|
|
|
244
|
-
A context window is a cost dial as much as a capacity number. OpenAI prices GPT-5.
|
|
245
|
-
prompts above **272,000 input tokens at 2x input and 1.5x output for the
|
|
246
|
-
request**, which is why the Codex catalog reports a 272,000 window rather than
|
|
247
|
-
model's ceiling.
|
|
248
|
-
|
|
244
|
+
A context window is a cost dial as much as a capacity number. OpenAI prices GPT-5.5
|
|
245
|
+
and later prompts above **272,000 input tokens at 2x input and 1.5x output for the
|
|
246
|
+
full request**, which is why the Codex catalog reports a 272,000 window rather than
|
|
247
|
+
the model's ceiling. Newer families inherit the same boundary, so a model released
|
|
248
|
+
after this was written is covered without a clodex update. Clodex follows that: the
|
|
249
|
+
default `standard` stop stays under the line, and a larger window is something you
|
|
250
|
+
ask for.
|
|
249
251
|
|
|
250
252
|
```sh
|
|
251
253
|
clodex models --context sol=max --save # this model's default, with a cost warning
|
|
@@ -253,19 +255,21 @@ clodex claude --context sol=max # this launch only, nothing saved
|
|
|
253
255
|
clodex models --context sol=default --save # back to the provider's tuned window
|
|
254
256
|
```
|
|
255
257
|
|
|
256
|
-
Each stop is reported with the numbers behind it: the raw window, the
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
258
|
+
Each stop is reported with the numbers behind it: the raw window, the effective
|
|
259
|
+
window a client should fill, and the account ceiling a larger stop can reach. A stop
|
|
260
|
+
above the ceiling is clamped and says so. When a request's own reported token count
|
|
261
|
+
crosses the boundary, clodex warns once per model for the life of the process,
|
|
262
|
+
because the client's token count and the provider's differ after translation and only
|
|
263
|
+
the provider's settles it.
|
|
262
264
|
|
|
263
265
|
Two things worth knowing about the numbers:
|
|
264
266
|
|
|
265
|
-
- **
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
267
|
+
- **Clodex reports the window the provider actually gives, and holds nothing back.**
|
|
268
|
+
Deciding how much of a window to leave free is the client's job — Claude Code
|
|
269
|
+
already reserves a fixed amount below whatever window it is told, and shrinking the
|
|
270
|
+
number first only costs usable context. A provider that declares a share of its own
|
|
271
|
+
is still honoured; clodex just never invents one. Use `--context` if you want a
|
|
272
|
+
smaller window than the provider offers.
|
|
269
273
|
- **The account ceiling moves.** It is server-side and per-account, and it has
|
|
270
274
|
changed by more than 2x within a single day in the past. `max` reads whatever the
|
|
271
275
|
catalog reports now and clamps to it, so a stale ceiling shrinks the stop rather
|
package/dist/cli.js
CHANGED
|
@@ -382,7 +382,7 @@ import { join } from "path";
|
|
|
382
382
|
// package.json
|
|
383
383
|
var package_default = {
|
|
384
384
|
name: "@bman654/clodex",
|
|
385
|
-
version: "2.
|
|
385
|
+
version: "2.11.1",
|
|
386
386
|
publishConfig: {
|
|
387
387
|
access: "public"
|
|
388
388
|
},
|
|
@@ -469,7 +469,7 @@ var package_default = {
|
|
|
469
469
|
|
|
470
470
|
// src/constants.ts
|
|
471
471
|
var CODEX_RESPONSES_LITE_WS_URL = "wss://chatgpt.com/backend-api/codex/responses";
|
|
472
|
-
var CODEX_RESPONSES_LITE_VERSION = "0.
|
|
472
|
+
var CODEX_RESPONSES_LITE_VERSION = "0.153.3";
|
|
473
473
|
var CODEX_RESPONSES_WEBSOCKETS_BETA = "responses_websockets=2026-02-06";
|
|
474
474
|
var TEST_TIMEOUT_MS = 1e4;
|
|
475
475
|
var CONFLICTING_ENV_VARS = [
|
|
@@ -3121,7 +3121,6 @@ async function deleteProviderCredential(authRef, diag) {
|
|
|
3121
3121
|
}
|
|
3122
3122
|
|
|
3123
3123
|
// src/context-modes.ts
|
|
3124
|
-
var DEFAULT_EFFECTIVE_CONTEXT_PERCENT = 95;
|
|
3125
3124
|
function positiveInteger(value) {
|
|
3126
3125
|
return typeof value === "number" && Number.isSafeInteger(value) && value > 0 ? value : void 0;
|
|
3127
3126
|
}
|
|
@@ -5873,6 +5872,18 @@ var CHATGPT_CODEX_UNSUPPORTED_MODELS = /* @__PURE__ */ new Set([
|
|
|
5873
5872
|
// confirmed: rejected by chatgpt.com/backend-api/codex
|
|
5874
5873
|
]);
|
|
5875
5874
|
var OPENAI_OAUTH_MODEL_SEEDS = [
|
|
5875
|
+
// GPT-6 family. The window and ceiling are what the live Codex catalog returned on
|
|
5876
|
+
// 2026-09-04 and are deliberately NOT the published API numbers: the model card
|
|
5877
|
+
// lists a 1,050,000 context window, but the Codex client is served a smaller one,
|
|
5878
|
+
// and this path is Codex-only. Output limit and the pricing band come from the
|
|
5879
|
+
// card (https://developers.openai.com/api/docs/models/gpt-6-astra), which states
|
|
5880
|
+
// "Prompts with more than 272K input tokens are priced at 2x input and cache rates
|
|
5881
|
+
// and 1.5x output for the full request" — the same boundary the GPT-5.6 family has.
|
|
5882
|
+
{ id: "gpt-6-astra", name: "GPT-6 Astra", contextWindow: 272e3, maxContextWindow: 872e3, maxOutputTokens: 128e3, reasoning: true, useResponsesLite: true, preferWebSockets: true },
|
|
5883
|
+
// "An alias for our flagship general-purpose models, with safeguards calibrated
|
|
5884
|
+
// for defensive cybersecurity work" — access is gated on a separate opt-in
|
|
5885
|
+
// program, so most installs will never see this id in their catalog.
|
|
5886
|
+
{ id: "gpt-daybreak-blue-latest", name: "GPT Daybreak Blue", contextWindow: 272e3, maxContextWindow: 872e3, maxOutputTokens: 128e3, reasoning: true, useResponsesLite: true, preferWebSockets: true },
|
|
5876
5887
|
// GPT-5.6 family (Sol / Terra / Luna)
|
|
5877
5888
|
{ id: "gpt-5.6-sol", name: "GPT-5.6 Sol", contextWindow: 272e3, maxContextWindow: 872e3, maxOutputTokens: 128e3, reasoning: true },
|
|
5878
5889
|
{ id: "gpt-5.6-terra", name: "GPT-5.6 Terra", contextWindow: 272e3, maxContextWindow: 872e3, maxOutputTokens: 128e3, reasoning: true },
|
|
@@ -5891,14 +5902,26 @@ var OPENAI_OAUTH_MODEL_SEEDS = [
|
|
|
5891
5902
|
{ id: "o1", name: "o1", reasoning: true },
|
|
5892
5903
|
{ id: "o1-mini", name: "o1 Mini", reasoning: true }
|
|
5893
5904
|
];
|
|
5894
|
-
|
|
5905
|
+
function hasPricingBoundary(id) {
|
|
5906
|
+
const version = /^gpt-(\d+)(?:\.(\d+))?(?:-|$)/i.exec(id);
|
|
5907
|
+
if (version) {
|
|
5908
|
+
const major = Number(version[1]);
|
|
5909
|
+
const minor = version[2] ? Number(version[2]) : 0;
|
|
5910
|
+
if (major > 5 || major === 5 && minor >= 5) return true;
|
|
5911
|
+
}
|
|
5912
|
+
return /^gpt-daybreak(?:-|$)/i.test(id);
|
|
5913
|
+
}
|
|
5895
5914
|
function openAiPricingMetadata(id) {
|
|
5896
|
-
if (!
|
|
5915
|
+
if (!hasPricingBoundary(id)) return {};
|
|
5897
5916
|
return {
|
|
5898
5917
|
pricingBoundary: GPT_5_6_PRICING_BOUNDARY,
|
|
5899
5918
|
pricingBoundaryNote: GPT_5_6_PRICING_NOTE
|
|
5900
5919
|
};
|
|
5901
5920
|
}
|
|
5921
|
+
var LEGACY_IMPOSED_CONTEXT_PERCENT = 95;
|
|
5922
|
+
function migratedEffectiveContextPercent(cached) {
|
|
5923
|
+
return cached === LEGACY_IMPOSED_CONTEXT_PERCENT ? void 0 : cached;
|
|
5924
|
+
}
|
|
5902
5925
|
function applyOAuthSeedContextMetadata(models) {
|
|
5903
5926
|
const seedById = new Map(buildOpenAiOAuthModels().map((model) => [model.id, model]));
|
|
5904
5927
|
return models.map((model) => {
|
|
@@ -5907,10 +5930,11 @@ function applyOAuthSeedContextMetadata(models) {
|
|
|
5907
5930
|
return {
|
|
5908
5931
|
...model,
|
|
5909
5932
|
maxContextWindow: model.maxContextWindow ?? seed?.maxContextWindow,
|
|
5910
|
-
effectiveContextPercent: model.effectiveContextPercent
|
|
5933
|
+
effectiveContextPercent: migratedEffectiveContextPercent(model.effectiveContextPercent),
|
|
5911
5934
|
pricingBoundary: model.pricingBoundary ?? seed?.pricingBoundary ?? pricing.pricingBoundary,
|
|
5912
5935
|
pricingBoundaryNote: model.pricingBoundaryNote ?? seed?.pricingBoundaryNote ?? pricing.pricingBoundaryNote,
|
|
5913
|
-
maxOutputTokens: model.maxOutputTokens ?? seed?.maxOutputTokens
|
|
5936
|
+
maxOutputTokens: model.maxOutputTokens ?? seed?.maxOutputTokens,
|
|
5937
|
+
reasoning: seed?.reasoning ?? model.reasoning
|
|
5914
5938
|
};
|
|
5915
5939
|
});
|
|
5916
5940
|
}
|
|
@@ -5926,7 +5950,7 @@ function buildOpenAiOAuthModels() {
|
|
|
5926
5950
|
brand: deriveBrand(prefix),
|
|
5927
5951
|
contextWindow: resolveContextWindow(seed.id, seed.contextWindow),
|
|
5928
5952
|
maxContextWindow: seed.maxContextWindow,
|
|
5929
|
-
effectiveContextPercent: seed.effectiveContextPercent
|
|
5953
|
+
effectiveContextPercent: seed.effectiveContextPercent,
|
|
5930
5954
|
pricingBoundary: seed.pricingBoundary ?? pricing.pricingBoundary,
|
|
5931
5955
|
pricingBoundaryNote: seed.pricingBoundaryNote ?? pricing.pricingBoundaryNote,
|
|
5932
5956
|
maxOutputTokens: seed.maxOutputTokens,
|
|
@@ -10253,7 +10277,7 @@ async function createLanguageModel(spec) {
|
|
|
10253
10277
|
}
|
|
10254
10278
|
var ANTHROPIC_EFFORT_LEVELS = ["low", "medium", "high"];
|
|
10255
10279
|
var OPENAI_EFFORT_LEVELS = ["low", "medium", "high", "xhigh"];
|
|
10256
|
-
var
|
|
10280
|
+
var CODEX_EXTENDED_EFFORT_LEVELS = ["none", "low", "medium", "high", "xhigh", "max"];
|
|
10257
10281
|
var GEMINI_EFFORT_LEVELS = ["low", "medium", "high"];
|
|
10258
10282
|
var MISTRAL_EFFORT_LEVELS = ["high", "off"];
|
|
10259
10283
|
var XAI_EFFORT_LEVELS = ["none", "low", "medium", "high"];
|
|
@@ -10417,14 +10441,32 @@ function mapCodexEffortToAnthropic(effort) {
|
|
|
10417
10441
|
return void 0;
|
|
10418
10442
|
}
|
|
10419
10443
|
}
|
|
10420
|
-
function
|
|
10421
|
-
return
|
|
10444
|
+
function isCodexReasoningFamily(modelId) {
|
|
10445
|
+
return !isChatVariant(modelId) && supportsExtendedCodexEffort(modelId);
|
|
10446
|
+
}
|
|
10447
|
+
function isChatVariant(modelId) {
|
|
10448
|
+
return /-chat(?:-|$)/i.test(modelId);
|
|
10449
|
+
}
|
|
10450
|
+
function supportsExtendedCodexEffort(modelId) {
|
|
10451
|
+
const version = /^gpt-(\d+)(?:\.(\d+))?(?:-|$)/i.exec(modelId);
|
|
10452
|
+
if (version) {
|
|
10453
|
+
const major = Number(version[1]);
|
|
10454
|
+
const minor = version[2] ? Number(version[2]) : 0;
|
|
10455
|
+
if (major > 5 || major === 5 && minor >= 6) return true;
|
|
10456
|
+
}
|
|
10457
|
+
return /^gpt-daybreak(?:-|$)/i.test(modelId);
|
|
10458
|
+
}
|
|
10459
|
+
function supportsNoneEffort(modelId) {
|
|
10460
|
+
return /^gpt-5\.6(?:-|$)/i.test(modelId) || /^gpt-daybreak-blue(?:-|$)/i.test(modelId);
|
|
10422
10461
|
}
|
|
10423
10462
|
function isReasoningSummaryUnsupportedModel(modelId) {
|
|
10424
10463
|
return /codex-spark(?:-|$)/i.test(modelId);
|
|
10425
10464
|
}
|
|
10426
10465
|
function mapCodexEffortToOpenAI(effort, modelId) {
|
|
10427
|
-
if (
|
|
10466
|
+
if (effort === "none") {
|
|
10467
|
+
return modelId && supportsNoneEffort(modelId) ? "none" : void 0;
|
|
10468
|
+
}
|
|
10469
|
+
if (modelId && supportsExtendedCodexEffort(modelId) && CODEX_EXTENDED_EFFORT_LEVELS.includes(effort)) {
|
|
10428
10470
|
return effort;
|
|
10429
10471
|
}
|
|
10430
10472
|
if (effort === "xhigh") return "high";
|
|
@@ -10565,9 +10607,9 @@ function getReasoningCapabilities(npm, modelId, metadata) {
|
|
|
10565
10607
|
}
|
|
10566
10608
|
if (npm === "@ai-sdk/openai" || npm === "@ai-sdk/azure") {
|
|
10567
10609
|
const prefersResponses = modelPrefersResponsesApi(modelId);
|
|
10568
|
-
if (prefersResponses || metadata?.reasoning) {
|
|
10610
|
+
if (prefersResponses || isCodexReasoningFamily(modelId) || metadata?.reasoning) {
|
|
10569
10611
|
return {
|
|
10570
|
-
levels:
|
|
10612
|
+
levels: supportsExtendedCodexEffort(modelId) ? CODEX_EXTENDED_EFFORT_LEVELS.filter((level) => level !== "none" || supportsNoneEffort(modelId)) : [...OPENAI_EFFORT_LEVELS],
|
|
10571
10613
|
defaultLevel: "medium",
|
|
10572
10614
|
supportsSummaries: true,
|
|
10573
10615
|
mode: "controllable",
|
|
@@ -10731,10 +10773,14 @@ function effortProviderOptions(npm, effort, modelId, metadata) {
|
|
|
10731
10773
|
return mapped ? { openrouter: { reasoning: { effort: mapped, exclude: false } } } : void 0;
|
|
10732
10774
|
}
|
|
10733
10775
|
if (npm === "@ai-sdk/openai" || npm === "@ai-sdk/azure") {
|
|
10734
|
-
if (!modelId ||
|
|
10776
|
+
if (!modelId || isChatVariant(modelId)) return void 0;
|
|
10777
|
+
if (!(modelPrefersResponsesApi(modelId) || isCodexReasoningFamily(modelId))) {
|
|
10778
|
+
return void 0;
|
|
10779
|
+
}
|
|
10735
10780
|
const reasoningEffort = mapCodexEffortToOpenAI(effort, modelId);
|
|
10736
10781
|
if (!reasoningEffort) return void 0;
|
|
10737
|
-
|
|
10782
|
+
const openaiOptions = { reasoningEffort, forceReasoning: true };
|
|
10783
|
+
return isReasoningSummaryUnsupportedModel(modelId) ? { openai: { ...openaiOptions, reasoningSummary: null } } : { openai: openaiOptions };
|
|
10738
10784
|
}
|
|
10739
10785
|
if (npm === "@ai-sdk/xai") {
|
|
10740
10786
|
if (!modelId || !isXaiReasoningEffortModel(modelId)) return void 0;
|
|
@@ -15021,7 +15067,7 @@ function parseOpenAiModelEntries(body) {
|
|
|
15021
15067
|
}
|
|
15022
15068
|
return [];
|
|
15023
15069
|
}
|
|
15024
|
-
function buildDynamicOAuthModel(entry, seedById) {
|
|
15070
|
+
function buildDynamicOAuthModel(entry, seedById, codexCatalog) {
|
|
15025
15071
|
const seed = seedById.get(entry.id);
|
|
15026
15072
|
if (seed) {
|
|
15027
15073
|
return {
|
|
@@ -15047,12 +15093,27 @@ function buildDynamicOAuthModel(entry, seedById) {
|
|
|
15047
15093
|
brand: deriveBrand(prefix),
|
|
15048
15094
|
contextWindow: entry.context_window ?? resolveContextWindow(id),
|
|
15049
15095
|
maxContextWindow: entry.max_context_window,
|
|
15050
|
-
|
|
15096
|
+
// Absent means no reduction. clodex reports the window the provider actually
|
|
15097
|
+
// gives; deciding how much of it to leave free is the client's job, and Claude
|
|
15098
|
+
// Code already reserves a flat 33,000 tokens below whatever it is told.
|
|
15099
|
+
effectiveContextPercent: entry.effective_context_window_percent,
|
|
15051
15100
|
maxOutputTokens: entry.max_output_tokens,
|
|
15052
15101
|
...openAiPricingMetadata(id),
|
|
15053
15102
|
modelFormat: "openai",
|
|
15054
15103
|
npm: "@ai-sdk/openai",
|
|
15055
|
-
|
|
15104
|
+
// Assume a model from the Codex listing reasons. That endpoint reports no
|
|
15105
|
+
// reasoning field of its own, so the old `modelPrefersResponsesApi(id)` was an
|
|
15106
|
+
// id-pattern GUESS that silently said "no" to every family it had not been
|
|
15107
|
+
// taught yet — gpt-6-astra and gpt-daybreak-blue-latest both landed as
|
|
15108
|
+
// non-reasoning that way, which dropped the user's chosen effort and removed
|
|
15109
|
+
// the effort selector from the patched binary (getPatchReasoningCapabilities
|
|
15110
|
+
// early-returns on a `false`). Verified against all 11 models in the live
|
|
15111
|
+
// catalog on 2026-09-04.
|
|
15112
|
+
//
|
|
15113
|
+
// This only decides what the effort UI offers. It is NOT on its own enough to
|
|
15114
|
+
// put reasoning.effort on the wire — effortProviderOptions admits by family —
|
|
15115
|
+
// so a wrong `true` here costs an unusable menu entry, not a 400.
|
|
15116
|
+
reasoning: codexCatalog ? true : modelPrefersResponsesApi(id),
|
|
15056
15117
|
useResponsesLite: entry.useResponsesLite,
|
|
15057
15118
|
preferWebSockets: entry.preferWebSockets
|
|
15058
15119
|
};
|
|
@@ -15081,7 +15142,7 @@ async function fetchJsonWithAuth(url, accessToken, timeoutMs) {
|
|
|
15081
15142
|
async function refreshOpenAiOAuthModels(accessToken) {
|
|
15082
15143
|
const TIMEOUT_MS = 1e4;
|
|
15083
15144
|
const seedById = new Map(buildOpenAiOAuthModels().map((m) => [m.id, m]));
|
|
15084
|
-
const toModels = (entries) => entries.map((entry) => buildDynamicOAuthModel(entry, seedById));
|
|
15145
|
+
const toModels = (entries, codexCatalog) => entries.map((entry) => buildDynamicOAuthModel(entry, seedById, codexCatalog));
|
|
15085
15146
|
const claudeVersion = getInstalledClaudeVersion();
|
|
15086
15147
|
const codexResult = await fetchJsonWithAuth(
|
|
15087
15148
|
`https://chatgpt.com/backend-api/codex/models?client_version=${claudeVersion}`,
|
|
@@ -15090,7 +15151,7 @@ async function refreshOpenAiOAuthModels(accessToken) {
|
|
|
15090
15151
|
);
|
|
15091
15152
|
const codexEntries = parseOpenAiModelEntries(codexResult.body);
|
|
15092
15153
|
if (codexEntries.length > 0) {
|
|
15093
|
-
return { models: toModels(codexEntries), source: "live" };
|
|
15154
|
+
return { models: toModels(codexEntries, true), source: "live" };
|
|
15094
15155
|
}
|
|
15095
15156
|
const chatGptResult = await fetchJsonWithAuth(
|
|
15096
15157
|
"https://chatgpt.com/backend-api/models",
|
|
@@ -15099,7 +15160,7 @@ async function refreshOpenAiOAuthModels(accessToken) {
|
|
|
15099
15160
|
);
|
|
15100
15161
|
const chatGptEntries = parseOpenAiModelEntries(chatGptResult.body).filter(({ id }) => !CHATGPT_CODEX_UNSUPPORTED_MODELS.has(id));
|
|
15101
15162
|
if (chatGptEntries.length > 0) {
|
|
15102
|
-
return { models: toModels(chatGptEntries), source: "live" };
|
|
15163
|
+
return { models: toModels(chatGptEntries, false), source: "live" };
|
|
15103
15164
|
}
|
|
15104
15165
|
const failures = [codexResult.error, chatGptResult.error].filter((error) => error !== void 0);
|
|
15105
15166
|
const credentialFailure = failures.find((error) => /(?:\brejected\b|\b401\b|\b403\b)/i.test(error));
|