@bman654/clodex 2.18.5 → 2.18.7
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 +12 -9
- package/dist/{chunk-YGEPQOPA.js → chunk-VFYRDLDD.js} +1 -1
- package/dist/chunk-VFYRDLDD.js.map +1 -0
- package/dist/claude-wrapper.js +1 -1
- package/dist/cli.js +101 -43
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-YGEPQOPA.js.map +0 -1
package/dist/cli.js
CHANGED
|
@@ -74,7 +74,7 @@ import {
|
|
|
74
74
|
withRegistryWriteLockSync,
|
|
75
75
|
writeParentNoticeLines,
|
|
76
76
|
writeParentNoticeLinesSync
|
|
77
|
-
} from "./chunk-
|
|
77
|
+
} from "./chunk-VFYRDLDD.js";
|
|
78
78
|
|
|
79
79
|
// src/cli.ts
|
|
80
80
|
import pc13 from "picocolors";
|
|
@@ -346,7 +346,7 @@ import { join } from "path";
|
|
|
346
346
|
// package.json
|
|
347
347
|
var package_default = {
|
|
348
348
|
name: "@bman654/clodex",
|
|
349
|
-
version: "2.18.
|
|
349
|
+
version: "2.18.7",
|
|
350
350
|
publishConfig: {
|
|
351
351
|
access: "public"
|
|
352
352
|
},
|
|
@@ -6062,16 +6062,33 @@ function compareCodexClientVersions(a, b) {
|
|
|
6062
6062
|
}
|
|
6063
6063
|
return 0;
|
|
6064
6064
|
}
|
|
6065
|
-
function
|
|
6066
|
-
|
|
6065
|
+
function atLeast(version, floor) {
|
|
6066
|
+
const order = compareCodexClientVersions(version, floor);
|
|
6067
|
+
return order === 0 || order === 1;
|
|
6068
|
+
}
|
|
6069
|
+
function codexClientRequirement(model) {
|
|
6070
|
+
if (!model.useResponsesLite) return void 0;
|
|
6067
6071
|
const minimum = readCodexClientVersion(model.minimalClientVersion);
|
|
6068
|
-
|
|
6072
|
+
const withheldAt = readCodexClientVersion(model.withheldAtClientVersion);
|
|
6073
|
+
const blockingMinimum = minimum !== void 0 && compareCodexClientVersions(minimum, CODEX_RESPONSES_LITE_VERSION) === 1 ? minimum : void 0;
|
|
6074
|
+
const blockingWithheld = withheldAt !== void 0 && atLeast(withheldAt, CODEX_RESPONSES_LITE_VERSION) ? withheldAt : void 0;
|
|
6075
|
+
if (blockingMinimum !== void 0 && (blockingWithheld === void 0 || compareCodexClientVersions(blockingMinimum, blockingWithheld) === 1)) {
|
|
6076
|
+
return { kind: "minimum", version: blockingMinimum };
|
|
6077
|
+
}
|
|
6078
|
+
return blockingWithheld === void 0 ? void 0 : { kind: "withheld", version: blockingWithheld };
|
|
6079
|
+
}
|
|
6080
|
+
function requiresNewerCodexClient(model) {
|
|
6081
|
+
return codexClientRequirement(model) !== void 0;
|
|
6069
6082
|
}
|
|
6070
6083
|
function codexClientVersionWarning(models) {
|
|
6071
|
-
const unavailable = models.
|
|
6084
|
+
const unavailable = models.flatMap((model) => {
|
|
6085
|
+
const requirement = codexClientRequirement(model);
|
|
6086
|
+
return requirement ? [{ id: model.id, requirement }] : [];
|
|
6087
|
+
});
|
|
6072
6088
|
if (unavailable.length === 0) return void 0;
|
|
6073
|
-
const details = unavailable.map((
|
|
6074
|
-
|
|
6089
|
+
const details = unavailable.map(({ id, requirement }) => `${printableServerText(id)} (requires ${requirement.kind === "minimum" ? requirement.version : `a version newer than ${requirement.version}`})`).join(", ");
|
|
6090
|
+
const reason = unavailable.every(({ requirement }) => requirement.kind === "minimum") ? "their catalog minimum exceeds this version" : "the ChatGPT catalog does not offer them to this version";
|
|
6091
|
+
return `Hidden ChatGPT-plan models: ${details}. clodex sends Codex client version ${CODEX_RESPONSES_LITE_VERSION} for Responses-Lite; ${reason}. Update clodex to a release supporting their required version to use them.`;
|
|
6075
6092
|
}
|
|
6076
6093
|
|
|
6077
6094
|
// src/data/openai-oauth-models.ts
|
|
@@ -15992,21 +16009,17 @@ var BLOCKED_HOSTNAMES = /* @__PURE__ */ new Set([
|
|
|
15992
16009
|
"169.254.169.254",
|
|
15993
16010
|
"metadata.google.internal",
|
|
15994
16011
|
"169.254.170.2",
|
|
15995
|
-
"fd00:ec2::254"
|
|
16012
|
+
"fd00:ec2::254",
|
|
16013
|
+
"100.100.100.200",
|
|
16014
|
+
// Alibaba Cloud ECS
|
|
16015
|
+
"fd20:ce::254"
|
|
16016
|
+
// Google Cloud IPv6
|
|
15996
16017
|
]);
|
|
15997
|
-
|
|
16018
|
+
var NON_PUBLIC_RANGES = /* @__PURE__ */ new Set(["loopback", "private", "uniqueLocal", "carrierGradeNat"]);
|
|
16019
|
+
function isBlockedIp(ipStr) {
|
|
15998
16020
|
try {
|
|
15999
16021
|
const ip = ipaddr.process(ipStr);
|
|
16000
|
-
|
|
16001
|
-
if (allowInsecureLocal && (range === "loopback" || range === "private")) {
|
|
16002
|
-
return false;
|
|
16003
|
-
}
|
|
16004
|
-
if (range === "loopback") return true;
|
|
16005
|
-
if (range === "private") return true;
|
|
16006
|
-
if (range === "linkLocal") return true;
|
|
16007
|
-
if (range === "uniqueLocal") return true;
|
|
16008
|
-
if (range === "carrierGradeNat") return true;
|
|
16009
|
-
return false;
|
|
16022
|
+
return ip.range() === "linkLocal" || BLOCKED_HOSTNAMES.has(ip.toString());
|
|
16010
16023
|
} catch {
|
|
16011
16024
|
return true;
|
|
16012
16025
|
}
|
|
@@ -16040,10 +16053,10 @@ async function validateCustomEndpointUrl(rawUrl, opts = {}) {
|
|
|
16040
16053
|
return {
|
|
16041
16054
|
ok: false,
|
|
16042
16055
|
error: "Only HTTPS URLs are allowed.",
|
|
16043
|
-
hint: "For
|
|
16056
|
+
hint: "For a server on this machine, your LAN or Tailscale (Ollama, LM Studio, vLLM), allow insecure HTTP when prompted."
|
|
16044
16057
|
};
|
|
16045
16058
|
} else if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
|
|
16046
|
-
return { ok: false, error: "URL must use https:// or user-approved http:// for local
|
|
16059
|
+
return { ok: false, error: "URL must use https:// or user-approved http:// for local, LAN or Tailscale servers." };
|
|
16047
16060
|
}
|
|
16048
16061
|
const rawHostname = parsed.hostname.toLowerCase();
|
|
16049
16062
|
const hostname = rawHostname.replace(/^\[(.*)\]$/, "$1");
|
|
@@ -16051,7 +16064,7 @@ async function validateCustomEndpointUrl(rawUrl, opts = {}) {
|
|
|
16051
16064
|
return {
|
|
16052
16065
|
ok: false,
|
|
16053
16066
|
error: "This URL points to a blocked internal/metadata host.",
|
|
16054
|
-
hint: "Use
|
|
16067
|
+
hint: "Use your model server's own loopback, LAN, Tailscale or public address."
|
|
16055
16068
|
};
|
|
16056
16069
|
}
|
|
16057
16070
|
const addresses = await resolveHostAddresses(hostname);
|
|
@@ -16068,19 +16081,18 @@ async function validateCustomEndpointUrl(rawUrl, opts = {}) {
|
|
|
16068
16081
|
} catch {
|
|
16069
16082
|
continue;
|
|
16070
16083
|
}
|
|
16071
|
-
if (isBlockedIp(addr
|
|
16084
|
+
if (isBlockedIp(addr)) {
|
|
16072
16085
|
return {
|
|
16073
16086
|
ok: false,
|
|
16074
|
-
error: "URL resolves to a
|
|
16075
|
-
hint: "
|
|
16087
|
+
error: "URL resolves to a restricted link-local or cloud metadata address.",
|
|
16088
|
+
hint: "Link-local addresses are not supported. Use the server's loopback, LAN, Tailscale or public address."
|
|
16076
16089
|
};
|
|
16077
16090
|
}
|
|
16078
16091
|
}
|
|
16079
16092
|
if (parsed.protocol === "http:") {
|
|
16080
16093
|
const allResolvedAddressesAreLocal = addresses.every((addr) => {
|
|
16081
16094
|
try {
|
|
16082
|
-
|
|
16083
|
-
return range === "loopback" || range === "private" || range === "uniqueLocal";
|
|
16095
|
+
return NON_PUBLIC_RANGES.has(ipaddr.process(addr).range());
|
|
16084
16096
|
} catch {
|
|
16085
16097
|
return false;
|
|
16086
16098
|
}
|
|
@@ -16088,8 +16100,8 @@ async function validateCustomEndpointUrl(rawUrl, opts = {}) {
|
|
|
16088
16100
|
if (!allowLocal || !allResolvedAddressesAreLocal) {
|
|
16089
16101
|
return {
|
|
16090
16102
|
ok: false,
|
|
16091
|
-
error: "HTTP is only allowed for
|
|
16092
|
-
hint: "Use https
|
|
16103
|
+
error: "HTTP is only allowed for addresses on this machine, your LAN or a private network such as Tailscale.",
|
|
16104
|
+
hint: "Use https:// for a server on the public internet."
|
|
16093
16105
|
};
|
|
16094
16106
|
}
|
|
16095
16107
|
}
|
|
@@ -16337,9 +16349,9 @@ async function runCustomEndpointAddFlow(onResult) {
|
|
|
16337
16349
|
}
|
|
16338
16350
|
let allowInsecureLocal = false;
|
|
16339
16351
|
if (/^http:\/\//i.test(String(baseUrl).trim())) {
|
|
16340
|
-
p3.log.warn("HTTP is not encrypted. Only use it for a trusted
|
|
16352
|
+
p3.log.warn("HTTP is not encrypted. Only use it for a trusted server on this machine, your LAN or Tailscale.");
|
|
16341
16353
|
const allow = await p3.confirm({
|
|
16342
|
-
message: "Allow insecure HTTP for this local
|
|
16354
|
+
message: "Allow insecure HTTP for this local, LAN or Tailscale server?",
|
|
16343
16355
|
initialValue: true
|
|
16344
16356
|
});
|
|
16345
16357
|
if (p3.isCancel(allow)) {
|
|
@@ -16629,12 +16641,7 @@ async function resolveRefreshCredentialWithSource(provider, resolveKey, selected
|
|
|
16629
16641
|
return resolved;
|
|
16630
16642
|
}
|
|
16631
16643
|
|
|
16632
|
-
// src/registry/
|
|
16633
|
-
async function refreshOAuthProvider(provider, accessToken) {
|
|
16634
|
-
const tpl = provider.templateId ?? provider.id;
|
|
16635
|
-
if (tpl === "openai" || tpl === "openai-oauth") return refreshOpenAiOAuthModels(accessToken);
|
|
16636
|
-
throw new Error(`refreshOAuthProvider: unsupported template "${tpl}"`);
|
|
16637
|
-
}
|
|
16644
|
+
// src/registry/openai-oauth-catalog.ts
|
|
16638
16645
|
function positiveInteger2(value) {
|
|
16639
16646
|
return typeof value === "number" && Number.isSafeInteger(value) && value > 0 ? value : void 0;
|
|
16640
16647
|
}
|
|
@@ -16754,7 +16761,41 @@ async function fetchJsonWithAuth(url, accessToken, timeoutMs) {
|
|
|
16754
16761
|
}
|
|
16755
16762
|
}
|
|
16756
16763
|
}
|
|
16757
|
-
async function
|
|
16764
|
+
async function markModelsWithheldAtRequestVersion(models, accessToken, timeoutMs, previous) {
|
|
16765
|
+
const pinned = await fetchJsonWithAuth(
|
|
16766
|
+
`https://chatgpt.com/backend-api/codex/models?client_version=${CODEX_RESPONSES_LITE_VERSION}`,
|
|
16767
|
+
accessToken,
|
|
16768
|
+
timeoutMs
|
|
16769
|
+
);
|
|
16770
|
+
const offered = readOfferedIds(pinned.body);
|
|
16771
|
+
if (!offered) return carryWithheldMarkers(models, previous);
|
|
16772
|
+
return models.map((model) => offered.has(model.id) ? model : { ...model, withheldAtClientVersion: CODEX_RESPONSES_LITE_VERSION });
|
|
16773
|
+
}
|
|
16774
|
+
function readOfferedIds(body) {
|
|
16775
|
+
if (!body || typeof body !== "object") return void 0;
|
|
16776
|
+
const record = body;
|
|
16777
|
+
const shape = Array.isArray(record.models) ? { rows: record.models, key: "slug" } : Array.isArray(record.data) ? { rows: record.data, key: "id" } : void 0;
|
|
16778
|
+
if (!shape || shape.rows.length === 0) return void 0;
|
|
16779
|
+
const ids = /* @__PURE__ */ new Set();
|
|
16780
|
+
for (const row of shape.rows) {
|
|
16781
|
+
const id = row !== null && typeof row === "object" ? row[shape.key] : void 0;
|
|
16782
|
+
if (typeof id !== "string" || id === "" || id !== id.trim()) return void 0;
|
|
16783
|
+
ids.add(id);
|
|
16784
|
+
}
|
|
16785
|
+
return ids;
|
|
16786
|
+
}
|
|
16787
|
+
function carryWithheldMarkers(models, previous) {
|
|
16788
|
+
const withheld = new Map(previous.flatMap((model) => {
|
|
16789
|
+
const version = readCodexClientVersion(model.withheldAtClientVersion);
|
|
16790
|
+
return version ? [[model.id, version]] : [];
|
|
16791
|
+
}));
|
|
16792
|
+
if (withheld.size === 0) return models;
|
|
16793
|
+
return models.map((model) => {
|
|
16794
|
+
const version = withheld.get(model.id);
|
|
16795
|
+
return version ? { ...model, withheldAtClientVersion: version } : model;
|
|
16796
|
+
});
|
|
16797
|
+
}
|
|
16798
|
+
async function refreshOpenAiOAuthModels(accessToken, previous = []) {
|
|
16758
16799
|
const TIMEOUT_MS = 1e4;
|
|
16759
16800
|
const seedById = new Map(buildOpenAiOAuthModels().map((m) => [m.id, m]));
|
|
16760
16801
|
const toModels = (entries, codexCatalog) => entries.map((entry) => buildDynamicOAuthModel(entry, seedById, codexCatalog));
|
|
@@ -16766,7 +16807,11 @@ async function refreshOpenAiOAuthModels(accessToken) {
|
|
|
16766
16807
|
);
|
|
16767
16808
|
const codexEntries = parseOpenAiModelEntries(codexResult.body);
|
|
16768
16809
|
if (codexEntries.length > 0) {
|
|
16769
|
-
|
|
16810
|
+
const models = toModels(codexEntries, true);
|
|
16811
|
+
return {
|
|
16812
|
+
models: await markModelsWithheldAtRequestVersion(models, accessToken, TIMEOUT_MS, previous),
|
|
16813
|
+
source: "live"
|
|
16814
|
+
};
|
|
16770
16815
|
}
|
|
16771
16816
|
const chatGptResult = await fetchJsonWithAuth(
|
|
16772
16817
|
"https://chatgpt.com/backend-api/models",
|
|
@@ -16775,7 +16820,10 @@ async function refreshOpenAiOAuthModels(accessToken) {
|
|
|
16775
16820
|
);
|
|
16776
16821
|
const chatGptEntries = parseOpenAiModelEntries(chatGptResult.body).filter(({ id }) => !CHATGPT_CODEX_UNSUPPORTED_MODELS.has(id));
|
|
16777
16822
|
if (chatGptEntries.length > 0) {
|
|
16778
|
-
return {
|
|
16823
|
+
return {
|
|
16824
|
+
models: carryWithheldMarkers(toModels(chatGptEntries, false), previous),
|
|
16825
|
+
source: "live"
|
|
16826
|
+
};
|
|
16779
16827
|
}
|
|
16780
16828
|
const failures = [codexResult.error, chatGptResult.error].filter((error) => error !== void 0);
|
|
16781
16829
|
const credentialFailure = failures.find((error) => /(?:\brejected\b|\b401\b|\b403\b)/i.test(error));
|
|
@@ -16786,6 +16834,15 @@ async function refreshOpenAiOAuthModels(accessToken) {
|
|
|
16786
16834
|
credentialRejected: credentialFailure !== void 0
|
|
16787
16835
|
};
|
|
16788
16836
|
}
|
|
16837
|
+
|
|
16838
|
+
// src/registry/refresh-models.ts
|
|
16839
|
+
async function refreshOAuthProvider(provider, accessToken) {
|
|
16840
|
+
const tpl = provider.templateId ?? provider.id;
|
|
16841
|
+
if (tpl === "openai" || tpl === "openai-oauth") {
|
|
16842
|
+
return refreshOpenAiOAuthModels(accessToken, provider.modelsCache?.models);
|
|
16843
|
+
}
|
|
16844
|
+
throw new Error(`refreshOAuthProvider: unsupported template "${tpl}"`);
|
|
16845
|
+
}
|
|
16789
16846
|
function warningSuffix(models) {
|
|
16790
16847
|
const warning = codexClientVersionWarning(applyOAuthSeedContextMetadata(models));
|
|
16791
16848
|
return warning ? ` ${warning}` : "";
|
|
@@ -16817,8 +16874,9 @@ async function refreshApiListProvider(provider, apiKey) {
|
|
|
16817
16874
|
let safeBaseUrl = baseUrl;
|
|
16818
16875
|
const templateDefault = catalogTemplate?.defaultBaseUrl?.trim();
|
|
16819
16876
|
if (configuredUrl && configuredUrl !== templateDefault) {
|
|
16877
|
+
const customEndpoint = provider.templateId === "custom-openai" || provider.templateId === "custom-anthropic";
|
|
16820
16878
|
const urlCheck = await validateCustomEndpointUrl(baseUrl, {
|
|
16821
|
-
allowInsecureLocal: catalogTemplate?.apiKeyOptional === true
|
|
16879
|
+
allowInsecureLocal: catalogTemplate?.apiKeyOptional === true || customEndpoint
|
|
16822
16880
|
});
|
|
16823
16881
|
if (!urlCheck.ok || !urlCheck.normalizedUrl) {
|
|
16824
16882
|
return { models: [], error: `${urlCheck.error ?? "Invalid API base URL."} ${urlCheck.hint ?? ""}`.trim() };
|
|
@@ -17016,7 +17074,7 @@ async function refreshProviderModels(providerId, apiKey, registry, credentialSna
|
|
|
17016
17074
|
}
|
|
17017
17075
|
} else {
|
|
17018
17076
|
const template = resolveProviderTemplate(provider);
|
|
17019
|
-
const keyOptional = template?.apiKeyOptional === true;
|
|
17077
|
+
const keyOptional = template?.apiKeyOptional === true || isAnonymousProvider(provider);
|
|
17020
17078
|
const effectiveKey = keyOptional && isLikelyPlaceholderKey(apiKey) ? "" : apiKey;
|
|
17021
17079
|
if (!keyOptional && isLikelyPlaceholderKey(effectiveKey)) {
|
|
17022
17080
|
if (cachedModelCount(cacheProvider) > 0) {
|