@bman654/clodex 2.17.0 → 2.18.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/dist/cli.js CHANGED
@@ -74,7 +74,7 @@ import {
74
74
  withRegistryWriteLockSync,
75
75
  writeParentNoticeLines,
76
76
  writeParentNoticeLinesSync
77
- } from "./chunk-5TG736TX.js";
77
+ } from "./chunk-YGEPQOPA.js";
78
78
 
79
79
  // src/cli.ts
80
80
  import pc13 from "picocolors";
@@ -249,7 +249,7 @@ function printFavoritesOnlyPanel() {
249
249
 
250
250
  // src/cli.ts
251
251
  import * as p13 from "@clack/prompts";
252
- import { realpathSync as realpathSync3 } from "fs";
252
+ import { realpathSync as realpathSync4 } from "fs";
253
253
  import { fileURLToPath as fileURLToPath2 } from "url";
254
254
 
255
255
  // src/launch.ts
@@ -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.17.0",
349
+ version: "2.18.1",
350
350
  publishConfig: {
351
351
  access: "public"
352
352
  },
@@ -401,10 +401,9 @@ var package_default = {
401
401
  "https-proxy-agent": "9.1.0",
402
402
  "ipaddr.js": "2.4.0",
403
403
  "node-forge": "1.4.0",
404
- "node-gyp-build": "4.8.4",
405
404
  open: "11.0.0",
406
405
  picocolors: "1.1.1",
407
- tweakcc: "4.3.0",
406
+ tweakcc: "4.3.3",
408
407
  undici: "7.29.0",
409
408
  ws: "8.21.0"
410
409
  },
@@ -3186,7 +3185,7 @@ function pricingBoundaryWarning(modelLabel, limits, resolved) {
3186
3185
  const boundary = positiveInteger(limits.pricingBoundary);
3187
3186
  if (boundary === void 0 || !resolved.crossesPricingBoundary) return null;
3188
3187
  const note = limits.pricingBoundaryNote?.trim();
3189
- return `${modelLabel}: a ${withThousands(resolved.effective)}-token window can grow past the ${withThousands(boundary)}-token pricing boundary.` + (note ? ` ${note}` : "") + " Use the standard stop to stay under it.";
3188
+ return `${modelLabel}: a ${withThousands(resolved.effective)}-token window can grow past the ${withThousands(boundary)}-token pricing boundary.` + (note ? ` ${note}` : "") + " Choose a smaller context stop to reduce the chance of higher-rate requests.";
3190
3189
  }
3191
3190
  function contextClampNotice(modelLabel, resolved) {
3192
3191
  if (resolved.clampedFrom === void 0) return null;
@@ -3867,18 +3866,18 @@ import {
3867
3866
  existsSync as existsSync4,
3868
3867
  mkdtempSync,
3869
3868
  mkdirSync as mkdirSync5,
3870
- readFileSync as readFileSync9,
3869
+ readFileSync as readFileSync10,
3871
3870
  renameSync as renameSync2,
3872
3871
  rmSync,
3873
3872
  statSync as statSync7,
3874
3873
  unlinkSync as unlinkSync3,
3875
3874
  writeFileSync as writeFileSync5,
3876
- openSync as openSync8,
3877
- closeSync as closeSync8,
3878
- realpathSync as realpathSync2
3875
+ openSync as openSync9,
3876
+ closeSync as closeSync9,
3877
+ realpathSync as realpathSync3
3879
3878
  } from "fs";
3880
- import { homedir as homedir2 } from "os";
3881
- import { basename, dirname as dirname5, join as join7 } from "path";
3879
+ import { homedir as homedir3 } from "os";
3880
+ import { basename, dirname as dirname5, join as join8 } from "path";
3882
3881
  import pc3 from "picocolors";
3883
3882
  import * as p2 from "@clack/prompts";
3884
3883
 
@@ -5979,6 +5978,69 @@ function deriveBrand(family) {
5979
5978
  return "Other";
5980
5979
  }
5981
5980
 
5981
+ // src/registry/server-text.ts
5982
+ function isControlCode(code) {
5983
+ return code <= 31 || code >= 127 && code <= 159;
5984
+ }
5985
+ function hasControlChars(value) {
5986
+ for (let i = 0; i < value.length; i++) {
5987
+ if (isControlCode(value.charCodeAt(i))) return true;
5988
+ }
5989
+ return false;
5990
+ }
5991
+ function printableServerText(value) {
5992
+ let out = "";
5993
+ for (const ch of value) out += isControlCode(ch.charCodeAt(0)) ? " " : ch;
5994
+ return out.trim();
5995
+ }
5996
+
5997
+ // src/codex-client-version.ts
5998
+ var VERSION2 = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[A-Za-z-][0-9A-Za-z-]*)(?:\.(?:0|[1-9]\d*|\d*[A-Za-z-][0-9A-Za-z-]*))*))?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$(?![\s\S])/;
5999
+ function readCodexClientVersion(value) {
6000
+ return typeof value === "string" && VERSION2.test(value) ? value : void 0;
6001
+ }
6002
+ function compareCodexClientVersions(a, b) {
6003
+ const left = VERSION2.exec(a);
6004
+ const right = VERSION2.exec(b);
6005
+ if (!left || !right) return void 0;
6006
+ for (let i = 1; i <= 3; i++) {
6007
+ const x = BigInt(left[i]);
6008
+ const y = BigInt(right[i]);
6009
+ if (x !== y) return x < y ? -1 : 1;
6010
+ }
6011
+ const preA = left[4];
6012
+ const preB = right[4];
6013
+ if (preA === preB) return 0;
6014
+ if (preA === void 0) return 1;
6015
+ if (preB === void 0) return -1;
6016
+ const partsA = preA.split(".");
6017
+ const partsB = preB.split(".");
6018
+ for (let i = 0; i < Math.max(partsA.length, partsB.length); i++) {
6019
+ const x = partsA[i];
6020
+ const y = partsB[i];
6021
+ if (x === void 0) return -1;
6022
+ if (y === void 0) return 1;
6023
+ if (x === y) continue;
6024
+ const numericX = /^\d+$/.test(x);
6025
+ const numericY = /^\d+$/.test(y);
6026
+ if (numericX && numericY) return BigInt(x) < BigInt(y) ? -1 : 1;
6027
+ if (numericX !== numericY) return numericX ? -1 : 1;
6028
+ return x < y ? -1 : 1;
6029
+ }
6030
+ return 0;
6031
+ }
6032
+ function requiresNewerCodexClient(model) {
6033
+ if (!model.useResponsesLite) return false;
6034
+ const minimum = readCodexClientVersion(model.minimalClientVersion);
6035
+ return minimum !== void 0 && compareCodexClientVersions(minimum, CODEX_RESPONSES_LITE_VERSION) === 1;
6036
+ }
6037
+ function codexClientVersionWarning(models) {
6038
+ const unavailable = models.filter(requiresNewerCodexClient);
6039
+ if (unavailable.length === 0) return void 0;
6040
+ const details = unavailable.map((model) => `${printableServerText(model.id)} (requires ${model.minimalClientVersion})`).join(", ");
6041
+ return `Hidden ChatGPT-plan models: ${details}. clodex sends Codex client version ${CODEX_RESPONSES_LITE_VERSION} for Responses-Lite; their catalog minimum exceeds this version. Update clodex to a release supporting their required version to use them.`;
6042
+ }
6043
+
5982
6044
  // src/data/openai-oauth-models.ts
5983
6045
  var GPT_HIGH_CONTEXT_PRICING_BOUNDARY = 272e3;
5984
6046
  var GPT_HIGH_CONTEXT_PRICING_NOTE = "Above it, OpenAI prices the full request at 2x input and 1.5x output.";
@@ -5993,19 +6055,19 @@ var OPENAI_OAUTH_MODEL_SEEDS = [
5993
6055
  // context window, but the Codex client is served a smaller one, and this path is
5994
6056
  // Codex-only. All three cards publish a 128,000 output limit and the same 272K
5995
6057
  // pricing boundary as the GPT-5.6 family.
5996
- { id: "gpt-6-astra", name: "GPT-6 Astra", contextWindow: 272e3, maxContextWindow: 872e3, maxOutputTokens: 128e3, reasoning: true, useResponsesLite: true, preferWebSockets: true },
5997
- { id: "gpt-6-sol", name: "GPT-6 Sol", contextWindow: 272e3, maxContextWindow: 872e3, maxOutputTokens: 128e3, reasoning: true, useResponsesLite: true, preferWebSockets: true },
5998
- { id: "gpt-6-luna", name: "GPT-6 Luna", contextWindow: 272e3, maxContextWindow: 872e3, maxOutputTokens: 128e3, reasoning: true, useResponsesLite: true, preferWebSockets: true },
6058
+ { id: "gpt-6-astra", name: "GPT-6 Astra", contextWindow: 272e3, maxContextWindow: 872e3, maxOutputTokens: 128e3, reasoning: true, useResponsesLite: true, preferWebSockets: true, minimalClientVersion: "0.153.0" },
6059
+ { id: "gpt-6-sol", name: "GPT-6 Sol", contextWindow: 272e3, maxContextWindow: 872e3, maxOutputTokens: 128e3, reasoning: true, useResponsesLite: true, preferWebSockets: true, minimalClientVersion: "0.155.0" },
6060
+ { id: "gpt-6-luna", name: "GPT-6 Luna", contextWindow: 272e3, maxContextWindow: 872e3, maxOutputTokens: 128e3, reasoning: true, useResponsesLite: true, preferWebSockets: true, minimalClientVersion: "0.155.0" },
5999
6061
  // "An alias for our flagship general-purpose models, with safeguards calibrated
6000
6062
  // for defensive cybersecurity work" — access is gated on a separate opt-in
6001
6063
  // program, so most installs will never see this id in their catalog.
6002
- { id: "gpt-daybreak-blue-latest", name: "GPT Daybreak Blue", contextWindow: 272e3, maxContextWindow: 872e3, maxOutputTokens: 128e3, reasoning: true, useResponsesLite: true, preferWebSockets: true },
6064
+ { id: "gpt-daybreak-blue-latest", name: "GPT Daybreak Blue", contextWindow: 272e3, maxContextWindow: 872e3, maxOutputTokens: 128e3, reasoning: true, useResponsesLite: true, preferWebSockets: true, minimalClientVersion: "0.144.0" },
6003
6065
  // GPT-5.6 family (Sol / Terra / Luna)
6004
- { id: "gpt-5.6-sol", name: "GPT-5.6 Sol", contextWindow: 272e3, maxContextWindow: 872e3, maxOutputTokens: 128e3, reasoning: true },
6005
- { id: "gpt-5.6-terra", name: "GPT-5.6 Terra", contextWindow: 272e3, maxContextWindow: 872e3, maxOutputTokens: 128e3, reasoning: true },
6006
- { id: "gpt-5.6-luna", name: "GPT-5.6 Luna", contextWindow: 272e3, maxContextWindow: 872e3, maxOutputTokens: 128e3, reasoning: true, useResponsesLite: true, preferWebSockets: true },
6066
+ { id: "gpt-5.6-sol", name: "GPT-5.6 Sol", contextWindow: 272e3, maxContextWindow: 872e3, maxOutputTokens: 128e3, reasoning: true, useResponsesLite: true, preferWebSockets: true, minimalClientVersion: "0.144.0" },
6067
+ { id: "gpt-5.6-terra", name: "GPT-5.6 Terra", contextWindow: 272e3, maxContextWindow: 872e3, maxOutputTokens: 128e3, reasoning: true, useResponsesLite: true, preferWebSockets: true, minimalClientVersion: "0.144.0" },
6068
+ { id: "gpt-5.6-luna", name: "GPT-5.6 Luna", contextWindow: 272e3, maxContextWindow: 872e3, maxOutputTokens: 128e3, reasoning: true, useResponsesLite: true, preferWebSockets: true, minimalClientVersion: "0.144.0" },
6007
6069
  // GPT-5.5 family (Pro)
6008
- { id: "gpt-5.5", name: "GPT-5.5", contextWindow: 272e3, maxOutputTokens: 128e3, reasoning: true },
6070
+ { id: "gpt-5.5", name: "GPT-5.5", contextWindow: 272e3, maxContextWindow: 272e3, maxOutputTokens: 128e3, reasoning: true, useResponsesLite: false, preferWebSockets: true, minimalClientVersion: "0.124.0" },
6009
6071
  // GPT-5.4 family
6010
6072
  { id: "gpt-5.4", name: "GPT-5.4", contextWindow: 272e3, maxContextWindow: 1e6 },
6011
6073
  { id: "gpt-5.4-mini", name: "GPT-5.4 Mini", contextWindow: 272e3 },
@@ -6045,12 +6107,16 @@ function applyOAuthSeedContextMetadata(models) {
6045
6107
  const pricing = openAiPricingMetadata(model.id);
6046
6108
  return {
6047
6109
  ...model,
6110
+ contextWindow: model.contextWindow ?? seed?.contextWindow,
6048
6111
  maxContextWindow: model.maxContextWindow ?? seed?.maxContextWindow,
6049
6112
  effectiveContextPercent: migratedEffectiveContextPercent(model.effectiveContextPercent),
6050
6113
  pricingBoundary: model.pricingBoundary ?? seed?.pricingBoundary ?? pricing.pricingBoundary,
6051
6114
  pricingBoundaryNote: model.pricingBoundaryNote ?? seed?.pricingBoundaryNote ?? pricing.pricingBoundaryNote,
6052
6115
  maxOutputTokens: model.maxOutputTokens ?? seed?.maxOutputTokens,
6053
- reasoning: seed?.reasoning ?? model.reasoning
6116
+ reasoning: seed?.reasoning ?? model.reasoning,
6117
+ useResponsesLite: model.useResponsesLite ?? seed?.useResponsesLite,
6118
+ preferWebSockets: model.preferWebSockets ?? seed?.preferWebSockets,
6119
+ minimalClientVersion: model.minimalClientVersion ?? seed?.minimalClientVersion
6054
6120
  };
6055
6121
  });
6056
6122
  }
@@ -6074,7 +6140,8 @@ function buildOpenAiOAuthModels() {
6074
6140
  npm: "@ai-sdk/openai",
6075
6141
  reasoning: seed.reasoning,
6076
6142
  useResponsesLite: seed.useResponsesLite,
6077
- preferWebSockets: seed.preferWebSockets
6143
+ preferWebSockets: seed.preferWebSockets,
6144
+ minimalClientVersion: seed.minimalClientVersion
6078
6145
  };
6079
6146
  });
6080
6147
  }
@@ -6104,22 +6171,6 @@ function normalizeGoogleDisplayName(rawName, bareId) {
6104
6171
  return stripGoogleModelPrefix(trimmed);
6105
6172
  }
6106
6173
 
6107
- // src/registry/server-text.ts
6108
- function isControlCode(code) {
6109
- return code <= 31 || code >= 127 && code <= 159;
6110
- }
6111
- function hasControlChars(value) {
6112
- for (let i = 0; i < value.length; i++) {
6113
- if (isControlCode(value.charCodeAt(i))) return true;
6114
- }
6115
- return false;
6116
- }
6117
- function printableServerText(value) {
6118
- let out = "";
6119
- for (const ch of value) out += isControlCode(ch.charCodeAt(0)) ? " " : ch;
6120
- return out.trim();
6121
- }
6122
-
6123
6174
  // src/trace-log.ts
6124
6175
  import {
6125
6176
  chmodSync as chmodSync3,
@@ -6590,6 +6641,7 @@ function parseModelList(body, npm, allowBareArray) {
6590
6641
  family,
6591
6642
  brand: deriveBrand(family),
6592
6643
  contextWindow,
6644
+ ...npm === "@ai-sdk/openai" ? openAiPricingMetadata(id) : {},
6593
6645
  cost,
6594
6646
  isFree: isFreeStatus(freeStatus),
6595
6647
  freeStatus,
@@ -6607,6 +6659,7 @@ function materializeTemplateModel(template, model, baseUrl) {
6607
6659
  const { id, upstreamModelId: normalizedUpstream } = normalizeGoogleModelId(model.id, npm);
6608
6660
  const family = model.family ?? (id.split(/[-/:]/)[0] ?? id);
6609
6661
  const freeStatus = model.freeStatus ?? classifyFreeStatus({ model });
6662
+ const pricing = npm === "@ai-sdk/openai" ? openAiPricingMetadata(model.upstreamModelId ?? id) : {};
6610
6663
  return {
6611
6664
  ...model,
6612
6665
  id,
@@ -6615,6 +6668,8 @@ function materializeTemplateModel(template, model, baseUrl) {
6615
6668
  family,
6616
6669
  brand: model.brand ?? deriveBrand(family),
6617
6670
  contextWindow: model.contextWindow ?? lookupKnownContextWindow(id),
6671
+ pricingBoundary: model.pricingBoundary ?? pricing.pricingBoundary,
6672
+ pricingBoundaryNote: model.pricingBoundaryNote ?? pricing.pricingBoundaryNote,
6618
6673
  isFree: model.isFree ?? isFreeStatus(freeStatus),
6619
6674
  freeStatus,
6620
6675
  modelFormat: model.modelFormat ?? modelFormatForNpm(npm),
@@ -6807,8 +6862,21 @@ function resolveEndpoint(npm, apiUrl) {
6807
6862
  }
6808
6863
  function projectProviderCachedModels(provider) {
6809
6864
  const cached = provider.modelsCache?.models ?? [];
6810
- if (isChatGptOAuthProvider(provider)) return applyOAuthSeedContextMetadata(cached);
6811
- if (!isRetainedOpenCodeGoProvider(provider)) return cached;
6865
+ if (isChatGptOAuthProvider(provider)) {
6866
+ return applyOAuthSeedContextMetadata(cached).filter((model) => !requiresNewerCodexClient(model));
6867
+ }
6868
+ if (!isRetainedOpenCodeGoProvider(provider)) {
6869
+ if (!cached.some((model) => (model.npm ?? provider.api.npm) === "@ai-sdk/openai")) return cached;
6870
+ return cached.map((model) => {
6871
+ if ((model.npm ?? provider.api.npm) !== "@ai-sdk/openai") return model;
6872
+ const pricing = openAiPricingMetadata(model.upstreamModelId ?? model.id);
6873
+ return {
6874
+ ...model,
6875
+ pricingBoundary: model.pricingBoundary ?? pricing.pricingBoundary,
6876
+ pricingBoundaryNote: model.pricingBoundaryNote ?? pricing.pricingBoundaryNote
6877
+ };
6878
+ });
6879
+ }
6812
6880
  const template = retainedOpenCodeGoTemplate();
6813
6881
  return template ? applyTemplateModelMetadata(template, cached) : [];
6814
6882
  }
@@ -6962,8 +7030,162 @@ function materializeRegistry(registry, resolveCredential, opts) {
6962
7030
  return result;
6963
7031
  }
6964
7032
 
7033
+ // src/editor-extension-version.ts
7034
+ import { execFileSync, execSync } from "child_process";
7035
+ import { readdirSync, readFileSync as readFileSync7, realpathSync } from "fs";
7036
+ import { homedir as homedir2 } from "os";
7037
+ import { join as join6 } from "path";
7038
+ var CLAUDE_EXTENSION_ID = "anthropic.claude-code";
7039
+ var EDITOR_EXTENSION_ROOTS = [
7040
+ { editor: "VS Code", dir: ".vscode" },
7041
+ { editor: "VS Code Insiders", dir: ".vscode-insiders" },
7042
+ { editor: "VS Code (remote server)", dir: ".vscode-server" },
7043
+ { editor: "VSCodium", dir: ".vscode-oss" },
7044
+ { editor: "Cursor", dir: ".cursor" },
7045
+ { editor: "Windsurf", dir: ".windsurf" }
7046
+ ];
7047
+ var CHECKED_EDITORS_DESCRIPTION = EDITOR_EXTENSION_ROOTS.map((root) => root.editor).join(", ");
7048
+ function releaseKey(version) {
7049
+ const match = /^(\d+)\.(\d+)\.(\d+)$/.exec(version.trim());
7050
+ return match ? match.slice(1).map(Number) : null;
7051
+ }
7052
+ function compareVersionKeys(a, b) {
7053
+ for (let i = 0; i < Math.max(a.length, b.length); i += 1) {
7054
+ const diff = (a[i] ?? 0) - (b[i] ?? 0);
7055
+ if (diff !== 0) return diff;
7056
+ }
7057
+ return 0;
7058
+ }
7059
+ function newestVersion(versions) {
7060
+ let best = null;
7061
+ for (const version of versions) {
7062
+ const key = releaseKey(version);
7063
+ if (key && (!best || compareVersionKeys(key, best.key) > 0)) best = { version, key };
7064
+ }
7065
+ return best?.version ?? null;
7066
+ }
7067
+ var EXTENSION_DIR_PATTERN = /^anthropic\.claude-code-(\d+\.\d+\.\d+)(?:-[a-z0-9]+)*$/i;
7068
+ function readJson(path) {
7069
+ try {
7070
+ return JSON.parse(readFileSync7(path, "utf8"));
7071
+ } catch {
7072
+ return void 0;
7073
+ }
7074
+ }
7075
+ function entryDirectoryName(entry) {
7076
+ if (typeof entry.relativeLocation === "string") return entry.relativeLocation;
7077
+ const location = entry.location;
7078
+ const raw = typeof location === "string" ? location : location && typeof location === "object" ? location.fsPath ?? location.path : void 0;
7079
+ if (typeof raw !== "string") return null;
7080
+ const parts = raw.split(/[\\/]/).filter(Boolean);
7081
+ return parts.at(-1) ?? null;
7082
+ }
7083
+ function readClaudeExtensionVersion(extensionsDir) {
7084
+ let names;
7085
+ try {
7086
+ names = readdirSync(extensionsDir);
7087
+ } catch {
7088
+ return null;
7089
+ }
7090
+ const present = new Set(names);
7091
+ const listed = readJson(join6(extensionsDir, "extensions.json"));
7092
+ if (Array.isArray(listed)) {
7093
+ const versions2 = [];
7094
+ for (const item of listed) {
7095
+ if (!item || typeof item !== "object") continue;
7096
+ const entry = item;
7097
+ const identifier = entry.identifier;
7098
+ if (typeof identifier?.id !== "string" || identifier.id.toLowerCase() !== CLAUDE_EXTENSION_ID) continue;
7099
+ if (typeof entry.version !== "string") continue;
7100
+ const dirName = entryDirectoryName(entry);
7101
+ if (dirName !== null && !present.has(dirName)) continue;
7102
+ versions2.push(entry.version);
7103
+ }
7104
+ return newestVersion(versions2);
7105
+ }
7106
+ const obsoleteRaw = readJson(join6(extensionsDir, ".obsolete"));
7107
+ const obsolete = obsoleteRaw && typeof obsoleteRaw === "object" && !Array.isArray(obsoleteRaw) ? new Set(Object.entries(obsoleteRaw).filter(([, flagged]) => flagged).map(([name]) => name)) : /* @__PURE__ */ new Set();
7108
+ const versions = [];
7109
+ for (const name of names) {
7110
+ if (obsolete.has(name)) continue;
7111
+ const match = EXTENSION_DIR_PATTERN.exec(name);
7112
+ if (match) versions.push(match[1]);
7113
+ }
7114
+ return newestVersion(versions);
7115
+ }
7116
+ function findInstalledClaudeExtensions(home = homedir2()) {
7117
+ const found = [];
7118
+ for (const root of EDITOR_EXTENSION_ROOTS) {
7119
+ const extensionsDir = join6(home, root.dir, "extensions");
7120
+ const version = readClaudeExtensionVersion(extensionsDir);
7121
+ if (version) found.push({ editor: root.editor, extensionsDir, version });
7122
+ }
7123
+ return found;
7124
+ }
7125
+ var NPM_ROOT_TIMEOUT_MS = 1e4;
7126
+ function readNpmGlobalRoot(env = process.env) {
7127
+ try {
7128
+ const options = {
7129
+ env,
7130
+ encoding: "utf8",
7131
+ stdio: ["ignore", "pipe", "ignore"],
7132
+ timeout: NPM_ROOT_TIMEOUT_MS
7133
+ };
7134
+ const output = process.platform === "win32" ? execSync("npm root -g", options) : execFileSync("npm", ["root", "-g"], options);
7135
+ const root = output.trim().split(/\r?\n/).at(-1)?.trim();
7136
+ return root || null;
7137
+ } catch {
7138
+ return null;
7139
+ }
7140
+ }
7141
+ function comparablePath(path) {
7142
+ let real = path;
7143
+ try {
7144
+ real = realpathSync(path);
7145
+ } catch {
7146
+ }
7147
+ const normalized = real.replace(/\\/g, "/").replace(/\/+$/, "");
7148
+ return /^[a-z]:\//i.test(normalized) ? normalized.toLowerCase() : normalized;
7149
+ }
7150
+ function claudeInstallCommand(binaryPath, version, npmGlobalRoot) {
7151
+ const path = comparablePath(binaryPath);
7152
+ if (/\/\.local\/share\/claude\/versions\/[^/]+$/.test(path)) return `claude install ${version}`;
7153
+ if (/\/node_modules\/@anthropic-ai\/claude-code/.test(path)) {
7154
+ const root = npmGlobalRoot();
7155
+ const packagePrefix = root === null ? null : `${comparablePath(root)}/@anthropic-ai/claude-code`;
7156
+ const underGlobalPackage = packagePrefix !== null && path.startsWith(packagePrefix) && ["/", "-"].includes(path.charAt(packagePrefix.length));
7157
+ if (underGlobalPackage) return `npm install -g @anthropic-ai/claude-code@${version}`;
7158
+ }
7159
+ return null;
7160
+ }
7161
+ function describeExtensionDrift(extension, patched, npmGlobalRoot) {
7162
+ const extensionKey = releaseKey(extension.version);
7163
+ const patchedKey = releaseKey(patched.version);
7164
+ if (!extensionKey || !patchedKey) return null;
7165
+ const order = compareVersionKeys(extensionKey, patchedKey);
7166
+ if (order === 0) return null;
7167
+ const { editor, version } = extension;
7168
+ const install = claudeInstallCommand(patched.binaryPath, version, npmGlobalRoot);
7169
+ const lines = [
7170
+ `${editor}'s Claude Code extension is ${version}, but the Claude Code clodex patched is ${patched.version} (${patched.binaryPath}).`,
7171
+ `When ${editor} launches Claude Code through clodex-claude (claudeCode.claudeProcessWrapper), its chats run the extension's own bundled ${version} binary and clodex models will not appear in its model picker.`,
7172
+ ...install ? ["To fix it, run:", ` ${install}`, " clodex patch"] : [`To fix it, update the Claude Code at ${patched.binaryPath} to ${version} with the tool that installed it, then run:`, " clodex patch"]
7173
+ ];
7174
+ if (order < 0) lines.push(`Or update the extension in ${editor} to ${patched.version}.`);
7175
+ return lines.join("\n");
7176
+ }
7177
+ function claudeExtensionDriftWarnings(patched, home = homedir2(), npmGlobalRoot = () => readNpmGlobalRoot()) {
7178
+ let root = null;
7179
+ const cachedRoot = () => (root ??= { value: npmGlobalRoot() }).value;
7180
+ try {
7181
+ return findInstalledClaudeExtensions(home).map((extension) => describeExtensionDrift(extension, patched, cachedRoot)).filter((warning) => warning !== null);
7182
+ } catch {
7183
+ return [];
7184
+ }
7185
+ }
7186
+
6965
7187
  // src/npm-shim.ts
6966
- import { readFileSync as readFileSync7, realpathSync, statSync as statSync4 } from "fs";
7188
+ import { readFileSync as readFileSync8, realpathSync as realpathSync2, statSync as statSync4 } from "fs";
6967
7189
  import { dirname as dirname3, resolve as resolve2 } from "path";
6968
7190
  var MAX_LAUNCHER_BYTES = 64 * 1024;
6969
7191
  var MAX_LAUNCHER_HOPS = 4;
@@ -6986,7 +7208,7 @@ function launcherKind(shimPath) {
6986
7208
  function readLauncherText(path) {
6987
7209
  try {
6988
7210
  if (statSync4(path).size > MAX_LAUNCHER_BYTES) return null;
6989
- return readFileSync7(path, "utf8");
7211
+ return readFileSync8(path, "utf8");
6990
7212
  } catch {
6991
7213
  return null;
6992
7214
  }
@@ -7043,7 +7265,7 @@ function resolveThroughNpmShims(startPath) {
7043
7265
  }
7044
7266
  let hopped;
7045
7267
  try {
7046
- hopped = realpathSync(reading.target);
7268
+ hopped = realpathSync2(reading.target);
7047
7269
  } catch {
7048
7270
  return {
7049
7271
  ok: false,
@@ -7068,13 +7290,13 @@ import {
7068
7290
  closeSync as closeSync2,
7069
7291
  fstatSync,
7070
7292
  openSync as openSync2,
7071
- readFileSync as readFileSync8,
7293
+ readFileSync as readFileSync9,
7072
7294
  readSync,
7073
7295
  statSync as statSync5
7074
7296
  } from "fs";
7075
7297
  import { createRequire } from "module";
7076
7298
  import { arch } from "os";
7077
- import { dirname as dirname4, join as join6 } from "path";
7299
+ import { dirname as dirname4, join as join7 } from "path";
7078
7300
  var MAX_PLACEHOLDER_BYTES = 64 * 1024;
7079
7301
  var CLAUDE_PACKAGE = "@anthropic-ai/claude-code";
7080
7302
  var MISSING_BINARY_MARKER = "claude native binary not installed";
@@ -7101,11 +7323,11 @@ function getPlatformKey() {
7101
7323
  }
7102
7324
  function inspectNativePackage(binaryPath) {
7103
7325
  const packageRoot2 = dirname4(dirname4(binaryPath));
7104
- const packageJsonPath = join6(packageRoot2, "package.json");
7105
- const installScriptPath = join6(packageRoot2, "install.cjs");
7326
+ const packageJsonPath = join7(packageRoot2, "package.json");
7327
+ const installScriptPath = join7(packageRoot2, "install.cjs");
7106
7328
  let manifest;
7107
7329
  try {
7108
- manifest = JSON.parse(readFileSync8(packageJsonPath, "utf8"));
7330
+ manifest = JSON.parse(readFileSync9(packageJsonPath, "utf8"));
7109
7331
  if (manifest.name !== CLAUDE_PACKAGE || !statSync5(installScriptPath).isFile()) {
7110
7332
  return { nativePackageState: "unknown", installScriptPath: null };
7111
7333
  }
@@ -7157,270 +7379,44 @@ function inspectClaudeNativeBinaryPlaceholder(path) {
7157
7379
  }
7158
7380
  }
7159
7381
 
7160
- // src/bun-entry-module.ts
7161
- import { closeSync as closeSync3, openSync as openSync3, readSync as readSync2, statSync as statSync6, writeSync } from "fs";
7162
- import { execFileSync } from "child_process";
7163
- var BUN_TRAILER = Buffer.from("\n---- Bun! ----\n");
7164
- var BUN_OFFSETS_BYTES = 32;
7165
- var TAIL_SCAN_BYTES = 16 * 1024 * 1024;
7166
- var MODULE_STRUCT_BYTES_CURRENT = 52;
7167
- var MODULE_STRUCT_BYTES_LEGACY = 36;
7168
- var CONTENTS_FIELD_AT = 8;
7169
- var SOURCEMAP_FIELD_AT = 16;
7170
- var BYTECODE_FIELD_AT = 24;
7171
- var MODULE_INFO_FIELD_AT = 32;
7172
- var BYTECODE_ORIGIN_PATH_FIELD_AT = 40;
7173
- var BUN_BLOB_FLAGS = {
7174
- /** Every module's source text lies in one run no other region falls inside. */
7175
- SOURCE_TEXT_CONTIGUOUS: 1 << 4,
7176
- /** A `[u32; modules]` of each module's source hash follows the module table. */
7177
- HAS_SOURCE_HASHES: 1 << 5,
7178
- /** After the source hashes: `u32 count`, then `count` x `{ u32 id, offset, length }`. */
7179
- HAS_BUILTIN_BYTECODE: 1 << 6,
7180
- /** After the builtin-bytecode table: one `{ offset, length }` for the shared string table. */
7181
- HAS_BYTECODE_STRING_TABLE: 1 << 7
7182
- };
7183
- var LOADER_FROM_END = 3;
7184
- var BUN_JAVASCRIPT_LOADER = 1;
7185
- var MAX_MODULE_NAME_BYTES = 4096;
7186
- var MIN_SHIMMABLE_NAME_BYTES = 7;
7187
- function tweakccRecognizesModuleName(name) {
7188
- return name.endsWith("/claude") || name === "claude" || name.endsWith("/claude.exe") || name === "claude.exe" || name.endsWith("/src/entrypoints/cli.js") || name === "src/entrypoints/cli.js";
7189
- }
7190
- function entryModuleShimName(byteLength) {
7191
- if (byteLength < MIN_SHIMMABLE_NAME_BYTES) return null;
7192
- const padding = byteLength - MIN_SHIMMABLE_NAME_BYTES;
7193
- return "/clodex".padEnd(padding, "-").slice(0, padding) + "/claude";
7194
- }
7195
- function readAt(fd, length, position) {
7196
- if (length <= 0 || position < 0) return null;
7197
- const buffer = Buffer.alloc(length);
7198
- const read = readSync2(fd, buffer, 0, length, position);
7199
- return read === length ? buffer : null;
7200
- }
7201
- function readBunModuleNames(fd, fileSize) {
7202
- const tailLength = Math.min(fileSize, TAIL_SCAN_BYTES);
7203
- const tail = readAt(fd, tailLength, fileSize - tailLength);
7204
- if (!tail) return null;
7205
- const tailAt = fileSize - tailLength;
7206
- for (let searchFrom = tail.length - 1; searchFrom >= 0; ) {
7207
- const trailerInTail = tail.lastIndexOf(BUN_TRAILER, searchFrom);
7208
- if (trailerInTail < 0) return null;
7209
- const parsed = parseBunModuleNamesAt(fd, tailAt + trailerInTail - BUN_OFFSETS_BYTES);
7210
- if (parsed) return parsed;
7211
- searchFrom = trailerInTail - 1;
7212
- }
7213
- return null;
7214
- }
7215
- function parseBunModuleNamesAt(fd, offsetsAt) {
7216
- try {
7217
- return parseBunModuleNamesAtUnchecked(fd, offsetsAt);
7218
- } catch {
7219
- return null;
7220
- }
7221
- }
7222
- function parseBunModuleNamesAtUnchecked(fd, offsetsAt) {
7223
- const offsets = readAt(fd, BUN_OFFSETS_BYTES, offsetsAt);
7224
- if (!offsets) return null;
7225
- const byteCount = offsets.readBigUInt64LE(0);
7226
- const modulesOffset = offsets.readUInt32LE(8);
7227
- const modulesLength = offsets.readUInt32LE(12);
7228
- const entryPointId = offsets.readUInt32LE(16);
7229
- const compileExecArgvOffset = offsets.readUInt32LE(20);
7230
- const compileExecArgvLength = offsets.readUInt32LE(24);
7231
- const flags = offsets.readUInt32LE(28);
7232
- if (byteCount <= 0n || byteCount > BigInt(offsetsAt)) return null;
7233
- const blobAt = offsetsAt - Number(byteCount);
7234
- if (modulesLength <= 0 || BigInt(modulesOffset) + BigInt(modulesLength) > byteCount) return null;
7235
- const structBytes = modulesLength % MODULE_STRUCT_BYTES_LEGACY === 0 && modulesLength % MODULE_STRUCT_BYTES_CURRENT !== 0 ? MODULE_STRUCT_BYTES_LEGACY : MODULE_STRUCT_BYTES_CURRENT;
7236
- const moduleCount = Math.floor(modulesLength / structBytes);
7237
- if (moduleCount <= 0 || entryPointId >= moduleCount) return null;
7238
- const modules = readAt(fd, moduleCount * structBytes, blobAt + modulesOffset);
7239
- if (!modules) return null;
7240
- const names = [];
7241
- const nameOffsets = [];
7242
- const contents = [];
7243
- const sourcemap = [];
7244
- const bytecode = [];
7245
- const moduleInfo = [];
7246
- const bytecodeOriginPath = [];
7247
- const loaders = [];
7248
- for (let index = 0; index < moduleCount; index++) {
7249
- const nameOffset = modules.readUInt32LE(index * structBytes);
7250
- const nameLength = modules.readUInt32LE(index * structBytes + 4);
7251
- if (nameLength <= 0 || nameLength > MAX_MODULE_NAME_BYTES) return null;
7252
- if (BigInt(nameOffset) + BigInt(nameLength) > byteCount) return null;
7253
- const bytes = readAt(fd, nameLength + 1, blobAt + nameOffset);
7254
- if (!bytes || bytes[nameLength] !== 0) return null;
7255
- const name = bytes.subarray(0, nameLength).toString("utf8");
7256
- if (!/^[\x20-\x7e]+$/.test(name)) return null;
7257
- names.push(name);
7258
- nameOffsets.push(blobAt + nameOffset);
7259
- const pairAt = (field) => ({
7260
- offset: modules.readUInt32LE(index * structBytes + field),
7261
- length: modules.readUInt32LE(index * structBytes + field + 4)
7262
- });
7263
- contents.push(pairAt(CONTENTS_FIELD_AT));
7264
- sourcemap.push(pairAt(SOURCEMAP_FIELD_AT));
7265
- bytecode.push(pairAt(BYTECODE_FIELD_AT));
7266
- const current = structBytes === MODULE_STRUCT_BYTES_CURRENT;
7267
- moduleInfo.push(current ? pairAt(MODULE_INFO_FIELD_AT) : { offset: 0, length: 0 });
7268
- bytecodeOriginPath.push(
7269
- current ? pairAt(BYTECODE_ORIGIN_PATH_FIELD_AT) : { offset: 0, length: 0 }
7270
- );
7271
- loaders.push(modules.readUInt8(index * structBytes + structBytes - LOADER_FROM_END));
7272
- }
7273
- return {
7274
- names,
7275
- entryPointId,
7276
- offsets: nameOffsets,
7277
- contents,
7278
- sourcemap,
7279
- bytecode,
7280
- moduleInfo,
7281
- bytecodeOriginPath,
7282
- loaders,
7283
- blobAt,
7284
- modulesAt: blobAt + modulesOffset,
7285
- modulesOffset,
7286
- modulesLength,
7287
- structBytes,
7288
- byteCount: Number(byteCount),
7289
- compileExecArgv: { offset: compileExecArgvOffset, length: compileExecArgvLength },
7290
- flags
7291
- };
7292
- }
7293
- function findStandIn(fd, fileSize, marker) {
7294
- const needle = Buffer.from(marker);
7295
- const chunkBytes = 8 * 1024 * 1024;
7296
- const offsets = [];
7297
- let carry = Buffer.alloc(0);
7298
- for (let position = 0; position < fileSize; ) {
7299
- const length = Math.min(chunkBytes, fileSize - position);
7300
- const chunk = readAt(fd, length, position);
7301
- if (!chunk) throw new Error("could not re-read the candidate to verify the entry-module name");
7302
- const window = carry.length > 0 ? Buffer.concat([carry, chunk]) : chunk;
7303
- const windowAt = position - carry.length;
7304
- for (let at = window.indexOf(needle); at >= 0; at = window.indexOf(needle, at + 1)) {
7305
- offsets.push(windowAt + at);
7306
- }
7307
- carry = Buffer.from(window.subarray(Math.max(0, window.length - (needle.length - 1))));
7308
- position += length;
7309
- }
7310
- return offsets;
7311
- }
7312
- function restoreEveryStandIn(fd, fileSize, shim) {
7313
- const moduleNameCopies = () => findStandIn(fd, fileSize, shim.marker).filter((offset) => isModuleNameAt(fd, offset, shim.marker));
7314
- const found = moduleNameCopies();
7315
- for (const offset of found) writeNameBytes(fd, shim.original, offset);
7316
- if (found.length > 0 && moduleNameCopies().length > 0) {
7317
- throw new Error(`the entry-module stand-in ${shim.marker} survived restoration`);
7318
- }
7319
- }
7320
- function isModuleNameAt(fd, offset, marker) {
7321
- const after = readAt(fd, 1, offset + Buffer.byteLength(marker));
7322
- return after !== null && after[0] === 0;
7323
- }
7324
- function writeNameBytes(fd, name, offset) {
7325
- const bytes = Buffer.from(name);
7326
- const written = writeSync(fd, bytes, 0, bytes.length, offset);
7327
- if (written !== bytes.length) {
7328
- throw new Error(`wrote ${written} of ${bytes.length} entry-module name bytes at ${offset}`);
7329
- }
7330
- }
7331
- function isMachO(fd) {
7332
- const magic = readAt(fd, 4, 0);
7333
- if (!magic) return false;
7334
- const value = magic.readUInt32BE(0);
7335
- return value === 4277009102 || value === 4277009103 || value === 3472551422 || value === 3489328638 || value === 3405691582 || value === 3405691583;
7336
- }
7337
- function readBunModuleTable(path) {
7338
- const fd = openSync3(path, "r");
7339
- try {
7340
- return readBunModuleNames(fd, statSync6(path).size);
7341
- } finally {
7342
- closeSync3(fd);
7343
- }
7344
- }
7345
- function readBunJavaScriptModules(path) {
7382
+ // src/patch-signature.ts
7383
+ import { execFileSync as execFileSync2 } from "child_process";
7384
+ import { closeSync as closeSync3, openSync as openSync3, readSync as readSync2 } from "fs";
7385
+ function signAndVerifyMachOCandidate(path, installPath = path) {
7386
+ if (process.platform !== "darwin") return;
7346
7387
  const fd = openSync3(path, "r");
7388
+ let machO = false;
7347
7389
  try {
7348
- const parsed = readBunModuleNames(fd, statSync6(path).size);
7349
- if (!parsed) return null;
7350
- const modules = [];
7351
- for (let index = 0; index < parsed.names.length; index++) {
7352
- if (parsed.loaders[index] !== BUN_JAVASCRIPT_LOADER) continue;
7353
- const range = parsed.contents[index];
7354
- if (range.offset < 0 || range.length < 0 || range.offset + range.length > parsed.byteCount) {
7355
- return null;
7356
- }
7357
- const bytes = range.length === 0 ? Buffer.alloc(0) : readAt(fd, range.length, parsed.blobAt + range.offset);
7358
- if (!bytes) return null;
7359
- modules.push({
7360
- index,
7361
- name: parsed.names[index],
7362
- source: bytes.toString("utf8"),
7363
- byteLength: bytes.length
7364
- });
7365
- }
7366
- return modules;
7367
- } finally {
7368
- closeSync3(fd);
7369
- }
7370
- }
7371
- function shimEntryModuleName(path) {
7372
- const fd = openSync3(path, "r+");
7373
- try {
7374
- const parsed = readBunModuleNames(fd, statSync6(path).size);
7375
- if (!parsed) return null;
7376
- if (parsed.names.some(tweakccRecognizesModuleName)) return null;
7377
- const original = parsed.names[parsed.entryPointId];
7378
- const offset = parsed.offsets[parsed.entryPointId];
7379
- const marker = entryModuleShimName(Buffer.byteLength(original));
7380
- if (marker === null) return null;
7381
- if (findStandIn(fd, statSync6(path).size, marker).some((offset2) => isModuleNameAt(fd, offset2, marker))) {
7382
- return null;
7383
- }
7384
- writeNameBytes(fd, marker, offset);
7385
- return { offset, original, marker };
7386
- } finally {
7387
- closeSync3(fd);
7388
- }
7389
- }
7390
- function restoreEntryModuleName(path, shim, { resign }) {
7391
- const fd = openSync3(path, "r+");
7392
- let machO;
7393
- try {
7394
- const parsed = readBunModuleNames(fd, statSync6(path).size);
7395
- const offset = parsed?.offsets[parsed.entryPointId];
7396
- if (!parsed || offset === void 0 || parsed.names[parsed.entryPointId] !== shim.marker) {
7397
- throw new Error(
7398
- `expected the entry module of ${path} to be named ${shim.marker}, found ${parsed ? JSON.stringify(parsed.names[parsed.entryPointId]) : "no readable Bun module list"}`
7399
- );
7390
+ const magic = Buffer.alloc(4);
7391
+ if (readSync2(fd, magic, 0, 4, 0) === 4) {
7392
+ machO = [
7393
+ 4277009102,
7394
+ 4277009103,
7395
+ 3472551422,
7396
+ 3489328638,
7397
+ 3405691582,
7398
+ 3405691583,
7399
+ 3199925962
7400
+ ].includes(magic.readUInt32BE(0));
7400
7401
  }
7401
- writeNameBytes(fd, shim.original, offset);
7402
- restoreEveryStandIn(fd, statSync6(path).size, shim);
7403
- machO = resign;
7404
7402
  } finally {
7405
7403
  closeSync3(fd);
7406
7404
  }
7407
- if (machO) resignMachOBinary(path);
7408
- }
7409
- function resignMachOBinary(path) {
7410
- const fd = openSync3(path, "r");
7411
- let machO;
7405
+ if (!machO) return;
7412
7406
  try {
7413
- machO = isMachO(fd);
7414
- } finally {
7415
- closeSync3(fd);
7416
- }
7417
- if (machO && process.platform === "darwin") {
7418
- execFileSync("codesign", ["-s", "-", "-f", path], { stdio: "ignore" });
7407
+ execFileSync2("codesign", ["-s", "-", "-f", path], { stdio: "pipe" });
7408
+ execFileSync2("codesign", ["--verify", "--strict", path], { stdio: "pipe" });
7409
+ } catch (error) {
7410
+ const stderr = error?.stderr;
7411
+ const detail = String(stderr ?? "").trim().split(`${path}: `).join("").split(/\r?\n/).filter((line) => line !== "replacing existing signature").join("\n").trim() || (error instanceof Error ? error.message.trimEnd() : String(error).trimEnd());
7412
+ throw new Error(
7413
+ `Mach-O signing or verification failed for ${installPath}: ${detail}. Claude Code was left unchanged.`
7414
+ );
7419
7415
  }
7420
7416
  }
7421
7417
 
7422
7418
  // src/bun-compiled-pointer.ts
7423
- import { closeSync as closeSync4, fstatSync as fstatSync2, openSync as openSync4, readSync as readSync3, writeSync as writeSync2 } from "fs";
7419
+ import { closeSync as closeSync4, fstatSync as fstatSync2, openSync as openSync4, readSync as readSync3, writeSync } from "fs";
7424
7420
  var TWEAKCC_SCAN_STRIDE = 16384n;
7425
7421
  var SCAN_CHUNK = 1 << 20;
7426
7422
  var ELF_MAGIC = 1179403647;
@@ -7428,7 +7424,7 @@ var ELFCLASS64 = 2;
7428
7424
  var ELFDATA2LSB = 1;
7429
7425
  var PT_LOAD = 1;
7430
7426
  var PF_W = 2;
7431
- function readAt2(fd, length, position) {
7427
+ function readAt(fd, length, position) {
7432
7428
  const buf = Buffer.alloc(length);
7433
7429
  let read = 0;
7434
7430
  while (read < length) {
@@ -7439,7 +7435,7 @@ function readAt2(fd, length, position) {
7439
7435
  return read === length ? buf : buf.subarray(0, read);
7440
7436
  }
7441
7437
  function readElfLayout(fd) {
7442
- const ident = readAt2(fd, 64, 0);
7438
+ const ident = readAt(fd, 64, 0);
7443
7439
  if (ident.length < 64) return null;
7444
7440
  if (ident.readUInt32LE(0) !== ELF_MAGIC) return null;
7445
7441
  if (ident[4] !== ELFCLASS64 || ident[5] !== ELFDATA2LSB) return null;
@@ -7455,7 +7451,7 @@ function readElfLayout(fd) {
7455
7451
  const fileSize = fstatSync2(fd).size;
7456
7452
  const fits = (offset, length) => Number.isSafeInteger(offset) && offset >= 0 && length >= 0 && offset + length <= fileSize;
7457
7453
  if (!fits(Number(shoff), shnum * shentsize)) return null;
7458
- const shTable = readAt2(fd, shnum * shentsize, Number(shoff));
7454
+ const shTable = readAt(fd, shnum * shentsize, Number(shoff));
7459
7455
  if (shTable.length !== shnum * shentsize) return null;
7460
7456
  const raw = [];
7461
7457
  for (let i = 0; i < shnum; i++) {
@@ -7469,7 +7465,7 @@ function readElfLayout(fd) {
7469
7465
  }
7470
7466
  const strtab = raw[shstrndx];
7471
7467
  if (!fits(Number(strtab.offset), Number(strtab.size))) return null;
7472
- const names = readAt2(fd, Number(strtab.size), Number(strtab.offset));
7468
+ const names = readAt(fd, Number(strtab.size), Number(strtab.offset));
7473
7469
  const sections = raw.map((section) => {
7474
7470
  const start = section.nameOffset;
7475
7471
  let end = start;
@@ -7482,7 +7478,7 @@ function readElfLayout(fd) {
7482
7478
  };
7483
7479
  });
7484
7480
  if (!fits(Number(phoff), phnum * phentsize)) return null;
7485
- const phTable = readAt2(fd, phnum * phentsize, Number(phoff));
7481
+ const phTable = readAt(fd, phnum * phentsize, Number(phoff));
7486
7482
  if (phTable.length !== phnum * phentsize) return null;
7487
7483
  const segments = [];
7488
7484
  for (let i = 0; i < phnum; i++) {
@@ -7519,7 +7515,7 @@ function tweakccWouldFind(fd, segment, needle) {
7519
7515
  const last = segment.vaddr + segment.filesz - 8n;
7520
7516
  for (let vaddr = first; vaddr <= last; vaddr += TWEAKCC_SCAN_STRIDE) {
7521
7517
  const at = Number(segment.offset + (vaddr - segment.vaddr));
7522
- if (readAt2(fd, 8, at).equals(needle)) return vaddr;
7518
+ if (readAt(fd, 8, at).equals(needle)) return vaddr;
7523
7519
  }
7524
7520
  return null;
7525
7521
  }
@@ -7528,7 +7524,7 @@ function scanForNeedle(fd, from, to, needle, skipFrom, skipTo) {
7528
7524
  for (let start = from; start < to; start += SCAN_CHUNK - 7) {
7529
7525
  const length = Math.min(SCAN_CHUNK, to - start);
7530
7526
  if (length < 8) break;
7531
- const chunk = readAt2(fd, length, start);
7527
+ const chunk = readAt(fd, length, start);
7532
7528
  let at = 0;
7533
7529
  for (; ; ) {
7534
7530
  const found = chunk.indexOf(needle, at);
@@ -7586,9 +7582,9 @@ function shimBunCompiledPointer(path) {
7586
7582
  throw new Error("no address tweakcc's ELF repack scans is usable for Bun's blob pointer");
7587
7583
  }
7588
7584
  const standInOffset = Number(segment.offset + (standInVaddr - segment.vaddr));
7589
- const displaced = readAt2(fd, 8, standInOffset);
7585
+ const displaced = readAt(fd, 8, standInOffset);
7590
7586
  if (displaced.length !== 8) throw new Error("could not read the bytes Bun's blob pointer displaces");
7591
- writeSync2(fd, needle, 0, 8, standInOffset);
7587
+ writeSync(fd, needle, 0, 8, standInOffset);
7592
7588
  return { pointerVaddr, standInVaddr, displaced, bunVaddr: bun.addr };
7593
7589
  } finally {
7594
7590
  closeSync4(fd);
@@ -7608,7 +7604,7 @@ function restoreBunCompiledPointer(path, shim) {
7608
7604
  "the repack left Bun's blob pointer outside the loaded image, or in more than one segment of it"
7609
7605
  );
7610
7606
  }
7611
- const standIn = readAt2(fd, 8, standInOffset);
7607
+ const standIn = readAt(fd, 8, standInOffset);
7612
7608
  if (standIn.length !== 8) {
7613
7609
  throw new Error("the repacked binary ends before the stand-in for Bun's blob pointer");
7614
7610
  }
@@ -7623,12 +7619,12 @@ function restoreBunCompiledPointer(path, shim) {
7623
7619
  }
7624
7620
  const value = Buffer.alloc(8);
7625
7621
  value.writeBigUInt64LE(written);
7626
- writeSync2(fd, value, 0, 8, pointerOffset);
7627
- writeSync2(fd, shim.displaced, 0, 8, standInOffset);
7628
- if (!readAt2(fd, 8, pointerOffset).equals(value)) {
7622
+ writeSync(fd, value, 0, 8, pointerOffset);
7623
+ writeSync(fd, shim.displaced, 0, 8, standInOffset);
7624
+ if (!readAt(fd, 8, pointerOffset).equals(value)) {
7629
7625
  throw new Error("Bun's blob pointer did not take the repacked address");
7630
7626
  }
7631
- if (!readAt2(fd, 8, standInOffset).equals(shim.displaced)) {
7627
+ if (!readAt(fd, 8, standInOffset).equals(shim.displaced)) {
7632
7628
  throw new Error("the bytes the stand-in displaced were not restored");
7633
7629
  }
7634
7630
  } finally {
@@ -7637,13 +7633,185 @@ function restoreBunCompiledPointer(path, shim) {
7637
7633
  }
7638
7634
 
7639
7635
  // src/bun-bundle.ts
7640
- import { closeSync as closeSync5, openSync as openSync5, readSync as readSync4, writeSync as writeSync3 } from "fs";
7636
+ import { closeSync as closeSync6, openSync as openSync6, readSync as readSync5, writeSync as writeSync2 } from "fs";
7637
+
7638
+ // src/bun-module-table.ts
7639
+ import { closeSync as closeSync5, openSync as openSync5, readSync as readSync4, statSync as statSync6 } from "fs";
7640
+ var BUN_TRAILER = Buffer.from("\n---- Bun! ----\n");
7641
+ var BUN_OFFSETS_BYTES = 32;
7642
+ var TAIL_SCAN_BYTES = 16 * 1024 * 1024;
7643
+ var MODULE_STRUCT_BYTES_CURRENT = 52;
7644
+ var MODULE_STRUCT_BYTES_LEGACY = 36;
7645
+ var CONTENTS_FIELD_AT = 8;
7646
+ var SOURCEMAP_FIELD_AT = 16;
7647
+ var BYTECODE_FIELD_AT = 24;
7648
+ var MODULE_INFO_FIELD_AT = 32;
7649
+ var BYTECODE_ORIGIN_PATH_FIELD_AT = 40;
7650
+ var BUN_BLOB_FLAGS = {
7651
+ /** Every module's source text lies in one run no other region falls inside. */
7652
+ SOURCE_TEXT_CONTIGUOUS: 1 << 4,
7653
+ /** A `[u32; modules]` of each module's source hash follows the module table. */
7654
+ HAS_SOURCE_HASHES: 1 << 5,
7655
+ /** After the source hashes: `u32 count`, then `count` x `{ u32 id, offset, length }`. */
7656
+ HAS_BUILTIN_BYTECODE: 1 << 6,
7657
+ /** After the builtin-bytecode table: one `{ offset, length }` for the shared string table. */
7658
+ HAS_BYTECODE_STRING_TABLE: 1 << 7
7659
+ };
7660
+ var LOADER_FROM_END = 3;
7661
+ var BUN_JAVASCRIPT_LOADER = 1;
7662
+ var MAX_MODULE_NAME_BYTES = 4096;
7663
+ function tweakccRecognizesModuleName(name) {
7664
+ return name.endsWith("/claude") || name === "claude" || name.endsWith("/claude.exe") || name === "claude.exe" || name.endsWith("/src/entrypoints/cli.js") || name === "src/entrypoints/cli.js" || name.endsWith("/cli") || name === "cli";
7665
+ }
7666
+ function readAt2(fd, length, position) {
7667
+ if (length <= 0 || position < 0) return null;
7668
+ const buffer = Buffer.alloc(length);
7669
+ const read = readSync4(fd, buffer, 0, length, position);
7670
+ return read === length ? buffer : null;
7671
+ }
7672
+ function readBunModuleNames(fd, fileSize) {
7673
+ const tailLength = Math.min(fileSize, TAIL_SCAN_BYTES);
7674
+ const tail = readAt2(fd, tailLength, fileSize - tailLength);
7675
+ if (!tail) return null;
7676
+ const tailAt = fileSize - tailLength;
7677
+ for (let searchFrom = tail.length - 1; searchFrom >= 0; ) {
7678
+ const trailerInTail = tail.lastIndexOf(BUN_TRAILER, searchFrom);
7679
+ if (trailerInTail < 0) return null;
7680
+ const parsed = parseBunModuleNamesAt(fd, tailAt + trailerInTail - BUN_OFFSETS_BYTES);
7681
+ if (parsed) return parsed;
7682
+ searchFrom = trailerInTail - 1;
7683
+ }
7684
+ return null;
7685
+ }
7686
+ function parseBunModuleNamesAt(fd, offsetsAt) {
7687
+ try {
7688
+ return parseBunModuleNamesAtUnchecked(fd, offsetsAt);
7689
+ } catch {
7690
+ return null;
7691
+ }
7692
+ }
7693
+ function parseBunModuleNamesAtUnchecked(fd, offsetsAt) {
7694
+ const offsets = readAt2(fd, BUN_OFFSETS_BYTES, offsetsAt);
7695
+ if (!offsets) return null;
7696
+ const byteCount = offsets.readBigUInt64LE(0);
7697
+ const modulesOffset = offsets.readUInt32LE(8);
7698
+ const modulesLength = offsets.readUInt32LE(12);
7699
+ const entryPointId = offsets.readUInt32LE(16);
7700
+ const compileExecArgvOffset = offsets.readUInt32LE(20);
7701
+ const compileExecArgvLength = offsets.readUInt32LE(24);
7702
+ const flags = offsets.readUInt32LE(28);
7703
+ if (byteCount <= 0n || byteCount > BigInt(offsetsAt)) return null;
7704
+ const blobAt = offsetsAt - Number(byteCount);
7705
+ if (modulesLength <= 0 || BigInt(modulesOffset) + BigInt(modulesLength) > byteCount) return null;
7706
+ const structBytes = modulesLength % MODULE_STRUCT_BYTES_LEGACY === 0 && modulesLength % MODULE_STRUCT_BYTES_CURRENT !== 0 ? MODULE_STRUCT_BYTES_LEGACY : MODULE_STRUCT_BYTES_CURRENT;
7707
+ const moduleCount = Math.floor(modulesLength / structBytes);
7708
+ if (moduleCount <= 0 || entryPointId >= moduleCount) return null;
7709
+ const modules = readAt2(fd, moduleCount * structBytes, blobAt + modulesOffset);
7710
+ if (!modules) return null;
7711
+ const names = [];
7712
+ const nameOffsets = [];
7713
+ const contents = [];
7714
+ const sourcemap = [];
7715
+ const bytecode = [];
7716
+ const moduleInfo = [];
7717
+ const bytecodeOriginPath = [];
7718
+ const loaders = [];
7719
+ for (let index = 0; index < moduleCount; index++) {
7720
+ const nameOffset = modules.readUInt32LE(index * structBytes);
7721
+ const nameLength = modules.readUInt32LE(index * structBytes + 4);
7722
+ if (nameLength <= 0 || nameLength > MAX_MODULE_NAME_BYTES) return null;
7723
+ if (BigInt(nameOffset) + BigInt(nameLength) > byteCount) return null;
7724
+ const bytes = readAt2(fd, nameLength + 1, blobAt + nameOffset);
7725
+ if (!bytes || bytes[nameLength] !== 0) return null;
7726
+ const name = bytes.subarray(0, nameLength).toString("utf8");
7727
+ if (!/^[\x20-\x7e]+$/.test(name)) return null;
7728
+ names.push(name);
7729
+ nameOffsets.push(blobAt + nameOffset);
7730
+ const pairAt = (field) => ({
7731
+ offset: modules.readUInt32LE(index * structBytes + field),
7732
+ length: modules.readUInt32LE(index * structBytes + field + 4)
7733
+ });
7734
+ contents.push(pairAt(CONTENTS_FIELD_AT));
7735
+ sourcemap.push(pairAt(SOURCEMAP_FIELD_AT));
7736
+ bytecode.push(pairAt(BYTECODE_FIELD_AT));
7737
+ const current = structBytes === MODULE_STRUCT_BYTES_CURRENT;
7738
+ moduleInfo.push(current ? pairAt(MODULE_INFO_FIELD_AT) : { offset: 0, length: 0 });
7739
+ bytecodeOriginPath.push(
7740
+ current ? pairAt(BYTECODE_ORIGIN_PATH_FIELD_AT) : { offset: 0, length: 0 }
7741
+ );
7742
+ loaders.push(modules.readUInt8(index * structBytes + structBytes - LOADER_FROM_END));
7743
+ }
7744
+ return {
7745
+ names,
7746
+ entryPointId,
7747
+ offsets: nameOffsets,
7748
+ contents,
7749
+ sourcemap,
7750
+ bytecode,
7751
+ moduleInfo,
7752
+ bytecodeOriginPath,
7753
+ loaders,
7754
+ blobAt,
7755
+ modulesAt: blobAt + modulesOffset,
7756
+ modulesOffset,
7757
+ modulesLength,
7758
+ structBytes,
7759
+ byteCount: Number(byteCount),
7760
+ compileExecArgv: { offset: compileExecArgvOffset, length: compileExecArgvLength },
7761
+ flags
7762
+ };
7763
+ }
7764
+ function readBunModuleTable(path) {
7765
+ const fd = openSync5(path, "r");
7766
+ try {
7767
+ return readBunModuleNames(fd, statSync6(path).size);
7768
+ } finally {
7769
+ closeSync5(fd);
7770
+ }
7771
+ }
7772
+ function readBunJavaScriptModules(path) {
7773
+ const fd = openSync5(path, "r");
7774
+ try {
7775
+ const parsed = readBunModuleNames(fd, statSync6(path).size);
7776
+ if (!parsed) return null;
7777
+ const modules = [];
7778
+ for (let index = 0; index < parsed.names.length; index++) {
7779
+ if (parsed.loaders[index] !== BUN_JAVASCRIPT_LOADER) continue;
7780
+ const range = parsed.contents[index];
7781
+ if (range.offset < 0 || range.length < 0 || range.offset + range.length > parsed.byteCount) {
7782
+ return null;
7783
+ }
7784
+ const bytes = range.length === 0 ? Buffer.alloc(0) : readAt2(fd, range.length, parsed.blobAt + range.offset);
7785
+ if (!bytes) return null;
7786
+ modules.push({
7787
+ index,
7788
+ name: parsed.names[index],
7789
+ source: bytes.toString("utf8"),
7790
+ byteLength: bytes.length
7791
+ });
7792
+ }
7793
+ return modules;
7794
+ } finally {
7795
+ closeSync5(fd);
7796
+ }
7797
+ }
7798
+
7799
+ // src/bun-bundle.ts
7641
7800
  function writableModuleIndex(path) {
7642
7801
  const table = readBunModuleTable(path);
7643
7802
  if (!table) return null;
7644
7803
  const index = table.names.findIndex(tweakccRecognizesModuleName);
7645
7804
  return index < 0 ? null : index;
7646
7805
  }
7806
+ function assertRecognizedEntryModule(path) {
7807
+ const table = readBunModuleTable(path);
7808
+ if (table && !table.names.some(tweakccRecognizesModuleName)) {
7809
+ const entry = table.names[table.entryPointId];
7810
+ throw new Error(
7811
+ `Claude Code entry module ${JSON.stringify(entry)} is not recognized by tweakcc. Update clodex and try again. Claude Code was left unchanged.`
7812
+ );
7813
+ }
7814
+ }
7647
7815
  var BUNDLE_MODULE_SEPARATOR = '\n/*clodex:module-boundary`;{}"]*/\n';
7648
7816
  function readClaudeBundle(path) {
7649
7817
  const modules = readBunJavaScriptModules(path);
@@ -7753,18 +7921,18 @@ function placeholderOf(byteLength) {
7753
7921
  return PLACEHOLDER_TEXT.repeat(Math.ceil(byteLength / PLACEHOLDER_TEXT.length)).slice(0, byteLength);
7754
7922
  }
7755
7923
  function readBlobData(path, table, into) {
7756
- const fd = openSync5(path, "r");
7924
+ const fd = openSync6(path, "r");
7757
7925
  try {
7758
7926
  let read = 0;
7759
7927
  while (read < table.byteCount) {
7760
- const got = readSync4(fd, into, read, table.byteCount - read, table.blobAt + read);
7928
+ const got = readSync5(fd, into, read, table.byteCount - read, table.blobAt + read);
7761
7929
  if (got <= 0) {
7762
7930
  throw new Error(`read ${read} of the ${table.byteCount} blob bytes of ${path}`);
7763
7931
  }
7764
7932
  read += got;
7765
7933
  }
7766
7934
  } finally {
7767
- closeSync5(fd);
7935
+ closeSync6(fd);
7768
7936
  }
7769
7937
  }
7770
7938
  function repointModule(data, table, append) {
@@ -7816,7 +7984,7 @@ function applyBundleWritePlan(path, plan) {
7816
7984
  }
7817
7985
  const offsets = Buffer.from(plan.offsets);
7818
7986
  offsets.writeBigUInt64LE(BigInt(byteCount), 0);
7819
- const fd = openSync5(path, "r+");
7987
+ const fd = openSync6(path, "r+");
7820
7988
  try {
7821
7989
  writeAll(fd, plan.data, repacked.blobAt, path);
7822
7990
  const padding = byteCount - plan.data.length;
@@ -7824,7 +7992,7 @@ function applyBundleWritePlan(path, plan) {
7824
7992
  writeAll(fd, offsets, repacked.blobAt + byteCount, path);
7825
7993
  writeAll(fd, BUN_TRAILER2, repacked.blobAt + byteCount + BUN_OFFSETS_BYTES2, path);
7826
7994
  } finally {
7827
- closeSync5(fd);
7995
+ closeSync6(fd);
7828
7996
  }
7829
7997
  const published = readBunModuleTable(path);
7830
7998
  if (!published) throw new Error(`cannot re-read the Bun module table of ${path} after publishing its blob`);
@@ -7852,7 +8020,7 @@ function applyBundleWritePlan(path, plan) {
7852
8020
  function writeAll(fd, bytes, position, path) {
7853
8021
  let written = 0;
7854
8022
  while (written < bytes.length) {
7855
- const wrote = writeSync3(fd, bytes, written, bytes.length - written, position + written);
8023
+ const wrote = writeSync2(fd, bytes, written, bytes.length - written, position + written);
7856
8024
  if (wrote <= 0) throw new Error(`wrote ${written} of ${bytes.length} blob bytes to ${path}`);
7857
8025
  written += wrote;
7858
8026
  }
@@ -7863,13 +8031,20 @@ import { wrapLanguageModel, extractReasoningMiddleware } from "ai";
7863
8031
 
7864
8032
  // src/oauth/responses-websocket.ts
7865
8033
  import { createHash as createHash5 } from "crypto";
7866
- import { closeSync as closeSync6, openSync as openSync6 } from "fs";
8034
+ import { closeSync as closeSync7, openSync as openSync7 } from "fs";
7867
8035
  import { devNull } from "os";
7868
8036
  import { AsyncLocalStorage } from "async_hooks";
7869
8037
 
7870
8038
  // src/outbound-proxy.ts
8039
+ import { randomUUID as randomUUID3 } from "crypto";
7871
8040
  import { networkInterfaces } from "os";
7872
8041
  import { HttpsProxyAgent } from "https-proxy-agent";
8042
+ var OUTBOUND_PROXY_HOP_HEADER = "x-clodex-proxy-hop";
8043
+ var outboundProxyHopNonce = randomUUID3();
8044
+ function isOwnOutboundProxyHop(value) {
8045
+ return (Array.isArray(value) ? value : [value]).some((entry) => entry?.split(",").some((part) => part.trim() === outboundProxyHopNonce));
8046
+ }
8047
+ var proxyHopHeaders = { [OUTBOUND_PROXY_HOP_HEADER]: outboundProxyHopNonce };
7873
8048
  function hasOutboundProxyEnv(env = process.env) {
7874
8049
  return Boolean(
7875
8050
  env["HTTPS_PROXY"]?.trim() || env["https_proxy"]?.trim() || env["HTTP_PROXY"]?.trim() || env["http_proxy"]?.trim()
@@ -7916,7 +8091,16 @@ function proxyUrlTargetsListener(proxyUrl, listenerHost, listenerPort, localAddr
7916
8091
  }
7917
8092
  const proxyPort = parsed.port ? Number(parsed.port) : parsed.protocol === "https:" ? 443 : parsed.protocol === "http:" ? 80 : void 0;
7918
8093
  if (proxyPort !== listenerPort) return false;
7919
- const normalizeHost = (host) => host.toLowerCase().replace(/^\[|\]$/g, "");
8094
+ const normalizeHost = (host) => {
8095
+ const plain = host.toLowerCase().replace(/^\[|\]$/g, "");
8096
+ const mapped = /^::ffff:([0-9a-f]{1,4}):([0-9a-f]{1,4})$/.exec(plain);
8097
+ if (mapped) {
8098
+ const high = parseInt(mapped[1], 16);
8099
+ const low = parseInt(mapped[2], 16);
8100
+ return `${high >> 8}.${high & 255}.${low >> 8}.${low & 255}`;
8101
+ }
8102
+ return plain.replace(/^::ffff:(?=\d+\.)/, "").replace(/^localhost\.$/, "localhost");
8103
+ };
7920
8104
  const proxyHost = normalizeHost(parsed.hostname);
7921
8105
  const boundHost = normalizeHost(listenerHost);
7922
8106
  if (proxyHost === boundHost) return true;
@@ -7949,7 +8133,7 @@ function outboundHttpProxyAgent(targetUrl, env = process.env) {
7949
8133
  if (!parsedProxy.hostname || !["http:", "https:"].includes(parsedProxy.protocol)) {
7950
8134
  throw new TypeError("Invalid proxy URL");
7951
8135
  }
7952
- return new HttpsProxyAgent(parsedProxy, { keepAlive: true });
8136
+ return new HttpsProxyAgent(parsedProxy, { keepAlive: true, headers: { ...proxyHopHeaders } });
7953
8137
  } catch (err) {
7954
8138
  emitParentNotice(
7955
8139
  `clodex: HTTP(S)_PROXY cannot be used for a CONNECT tunnel; using a direct connection (${err instanceof Error ? err.message : String(err)})`
@@ -9665,7 +9849,7 @@ function descriptorExhaustionCode(error) {
9665
9849
  const code = error.code;
9666
9850
  if (isDescriptorExhaustion(code)) return code;
9667
9851
  try {
9668
- closeSync6(openSync6(devNull, "r"));
9852
+ closeSync7(openSync7(devNull, "r"));
9669
9853
  return void 0;
9670
9854
  } catch (probe) {
9671
9855
  const probeCode = probe.code;
@@ -10561,7 +10745,7 @@ function createResponsesWebSocketFetch(wsUrl, log13, options = {}) {
10561
10745
  }
10562
10746
 
10563
10747
  // src/oauth/claude-identity.ts
10564
- import { createHash as createHash6, randomUUID as randomUUID3 } from "crypto";
10748
+ import { createHash as createHash6, randomUUID as randomUUID4 } from "crypto";
10565
10749
  var CLAUDE_CODE_CLI_VERSION = "2.1.195";
10566
10750
  var CLAUDE_CODE_USER_AGENT = `claude-cli/${CLAUDE_CODE_CLI_VERSION} (external, cli)`;
10567
10751
  var CLAUDE_CODE_ENTRYPOINT = process.env.CLAUDE_CODE_ENTRYPOINT ?? "cli";
@@ -10570,7 +10754,7 @@ var sessionCache = /* @__PURE__ */ new Map();
10570
10754
  function getOrCreateSessionId(seed) {
10571
10755
  let id = sessionCache.get(seed);
10572
10756
  if (!id) {
10573
- id = randomUUID3();
10757
+ id = randomUUID4();
10574
10758
  sessionCache.set(seed, id);
10575
10759
  }
10576
10760
  return id;
@@ -11494,7 +11678,7 @@ function thinkingProviderOptions(npm) {
11494
11678
 
11495
11679
  // src/proxy.ts
11496
11680
  import { createServer } from "http";
11497
- import { appendFileSync as appendFileSync2, openSync as openSync7, writeSync as writeSync4, closeSync as closeSync7 } from "fs";
11681
+ import { appendFileSync as appendFileSync2, openSync as openSync8, writeSync as writeSync3, closeSync as closeSync8 } from "fs";
11498
11682
 
11499
11683
  // src/http-utils.ts
11500
11684
  import * as zlib from "zlib";
@@ -11705,7 +11889,7 @@ function withThousands2(value) {
11705
11889
  return value.toLocaleString("en-US");
11706
11890
  }
11707
11891
  function formatPricingBoundaryWarning(modelLabel, boundary, inputTokens) {
11708
- return `clodex: ${modelLabel} request counted ${withThousands2(inputTokens)} input tokens, above the ${withThousands2(boundary)}-token pricing boundary. The provider prices the full request at a higher rate above this point. Set this model to the standard context stop to stay under it.`;
11892
+ return `clodex: ${modelLabel} request counted ${withThousands2(inputTokens)} input tokens, above the ${withThousands2(boundary)}-token pricing boundary. The provider prices the full request at a higher rate above this point. Choose a smaller context stop to reduce the chance of higher-rate requests.`;
11709
11893
  }
11710
11894
  function reportPricingBoundaryCrossing(observation, emit = emitParentNotice) {
11711
11895
  const { modelKey, modelLabel, pricingBoundary, inputTokens } = observation;
@@ -11726,7 +11910,7 @@ function routeUnavailableMessage(modelId, reason) {
11726
11910
  }
11727
11911
 
11728
11912
  // src/upstream-forward.ts
11729
- import { randomUUID as randomUUID4 } from "crypto";
11913
+ import { randomUUID as randomUUID5 } from "crypto";
11730
11914
  import { Readable, Transform } from "stream";
11731
11915
  import { StringDecoder } from "string_decoder";
11732
11916
 
@@ -11823,7 +12007,7 @@ function upstreamHoldsThreads(url) {
11823
12007
  }
11824
12008
  }
11825
12009
  function anchorSafeMessageId(id) {
11826
- return typeof id === "string" && id.startsWith("msg_") ? "clodex_" + randomUUID4().replace(/-/g, "") : id;
12010
+ return typeof id === "string" && id.startsWith("msg_") ? "clodex_" + randomUUID5().replace(/-/g, "") : id;
11827
12011
  }
11828
12012
  function isThreadContinuation(body) {
11829
12013
  const thread = body.thread;
@@ -11972,10 +12156,10 @@ async function relayAnthropicMessages(res, messagesUrl, body, apiKey, clientWant
11972
12156
  }
11973
12157
 
11974
12158
  // src/proxy.ts
11975
- import { randomUUID as randomUUID6 } from "crypto";
12159
+ import { randomUUID as randomUUID7 } from "crypto";
11976
12160
 
11977
12161
  // src/sdk-adapter.ts
11978
- import { createHash as createHash7, randomUUID as randomUUID5 } from "crypto";
12162
+ import { createHash as createHash7, randomUUID as randomUUID6 } from "crypto";
11979
12163
  import { streamText, generateText, tool, jsonSchema } from "ai";
11980
12164
 
11981
12165
  // src/proxy-shared.ts
@@ -12515,15 +12699,10 @@ function annotateToolNames(messages) {
12515
12699
  }
12516
12700
  }
12517
12701
  function thinkingToSdkPart(block, npm) {
12518
- const text6 = block.thinking ?? "";
12519
- if (npm === "@ai-sdk/openai" && !block.signature && !text6.trim()) return null;
12520
- const part = { type: "reasoning", text: text6 };
12521
- if (block.signature) {
12522
- if (npm === "@ai-sdk/google") {
12523
- part.providerOptions = { google: { thoughtSignature: block.signature } };
12524
- } else if (npm === "@ai-sdk/openai" || npm === "@ai-sdk/openai-compatible") {
12525
- part.providerOptions = { openai: { reasoningEncryptedContent: block.signature } };
12526
- }
12702
+ if (npm === "@ai-sdk/openai") return null;
12703
+ const part = { type: "reasoning", text: block.thinking ?? "" };
12704
+ if (block.signature && npm === "@ai-sdk/google") {
12705
+ part.providerOptions = { google: { thoughtSignature: block.signature } };
12527
12706
  }
12528
12707
  return part;
12529
12708
  }
@@ -12824,7 +13003,7 @@ function forwardAbortSignal(source, target) {
12824
13003
  return () => source.removeEventListener("abort", forward);
12825
13004
  }
12826
13005
  function translatedMessageId() {
12827
- return "clodex_" + randomUUID5().replace(/-/g, "");
13006
+ return "clodex_" + randomUUID6().replace(/-/g, "");
12828
13007
  }
12829
13008
  async function writeAnthropicStream(stream, modelId, write, log13, observer, tools) {
12830
13009
  const messageId = translatedMessageId();
@@ -13349,12 +13528,12 @@ function createTranslationLifecycle(logPath, requestId, claudeSessionId, modelId
13349
13528
  function appendSecureLog(logPath, line) {
13350
13529
  const redacted = redactTraceLine(line);
13351
13530
  try {
13352
- const fd = openSync7(logPath, "a", 384);
13531
+ const fd = openSync8(logPath, "a", 384);
13353
13532
  try {
13354
- writeSync4(fd, `${(/* @__PURE__ */ new Date()).toISOString()} ${redacted}
13533
+ writeSync3(fd, `${(/* @__PURE__ */ new Date()).toISOString()} ${redacted}
13355
13534
  `);
13356
13535
  } finally {
13357
- closeSync7(fd);
13536
+ closeSync8(fd);
13358
13537
  }
13359
13538
  } catch {
13360
13539
  try {
@@ -13401,7 +13580,7 @@ function configuredAliasLookupNames(alias) {
13401
13580
  }))].map(normalizeRouteLookupId);
13402
13581
  }
13403
13582
  async function startProxyCatalog(routes, defaultAliasId, debug = false, inferenceLogPath, debugLogPath, webSocketDiagnosticsLogPath, modelAliases) {
13404
- const proxyToken = randomUUID6();
13583
+ const proxyToken = randomUUID7();
13405
13584
  silenceSdkWarnings();
13406
13585
  if (routes.length === 0) {
13407
13586
  throw new Error("Proxy catalog requires at least one route");
@@ -13824,7 +14003,7 @@ async function startProxyCatalog(routes, defaultAliasId, debug = false, inferenc
13824
14003
  res,
13825
14004
  upstreamStatus === 500 ? 502 : upstreamStatus,
13826
14005
  clientMessage,
13827
- contextLengthExceeded ? relayRequestId ?? randomUUID6() : void 0
14006
+ contextLengthExceeded ? relayRequestId ?? randomUUID7() : void 0
13828
14007
  );
13829
14008
  } else {
13830
14009
  const errorType = anthropicErrorType(upstreamStatus, details?.transportCode);
@@ -13832,7 +14011,7 @@ async function startProxyCatalog(routes, defaultAliasId, debug = false, inferenc
13832
14011
  data: ${JSON.stringify({
13833
14012
  type: "error",
13834
14013
  error: { type: errorType, message: clientMessage },
13835
- ...contextLengthExceeded ? { request_id: relayRequestId ?? randomUUID6() } : {}
14014
+ ...contextLengthExceeded ? { request_id: relayRequestId ?? randomUUID7() } : {}
13836
14015
  })}
13837
14016
 
13838
14017
  `);
@@ -14062,7 +14241,7 @@ function buildHttpProxyRoutes(providers, favorites, modelAliases = void 0, max =
14062
14241
 
14063
14242
  // src/patcher.ts
14064
14243
  function getPatchLockPath() {
14065
- return join7(getAppHome(), "patch.lock");
14244
+ return join8(getAppHome(), "patch.lock");
14066
14245
  }
14067
14246
  function writePatchManifest(manifest, path = getPatchManifestPath()) {
14068
14247
  mkdirSync5(getAppHome(), { recursive: true, mode: 448 });
@@ -14219,13 +14398,13 @@ function pidIsAlive(pid) {
14219
14398
  function tryAcquirePatchLock(lockPath = getPatchLockPath(), opts = {}) {
14220
14399
  const now = opts.now ?? Date.now();
14221
14400
  const isAlive = opts.isAlive ?? pidIsAlive;
14222
- mkdirSync5(join7(lockPath, ".."), { recursive: true, mode: 448 });
14401
+ mkdirSync5(join8(lockPath, ".."), { recursive: true, mode: 448 });
14223
14402
  for (let attempt = 0; attempt < 2; attempt++) {
14224
14403
  try {
14225
- const fd = openSync8(lockPath, "wx");
14404
+ const fd = openSync9(lockPath, "wx");
14226
14405
  const content = { pid: process.pid, startedAt: now };
14227
14406
  writeFileSync5(fd, JSON.stringify(content));
14228
- closeSync8(fd);
14407
+ closeSync9(fd);
14229
14408
  return () => {
14230
14409
  try {
14231
14410
  unlinkSync3(lockPath);
@@ -14235,7 +14414,7 @@ function tryAcquirePatchLock(lockPath = getPatchLockPath(), opts = {}) {
14235
14414
  } catch {
14236
14415
  let stale = false;
14237
14416
  try {
14238
- const existing = JSON.parse(readFileSync9(lockPath, "utf8"));
14417
+ const existing = JSON.parse(readFileSync10(lockPath, "utf8"));
14239
14418
  stale = !existing.pid || !isAlive(existing.pid) || typeof existing.startedAt === "number" && now - existing.startedAt > PATCH_LOCK_STALE_MS;
14240
14419
  } catch {
14241
14420
  stale = true;
@@ -14257,7 +14436,7 @@ function takeIgnoredLaunchOverride() {
14257
14436
  }
14258
14437
  function resolveClaudeBinaryForPatch() {
14259
14438
  const envOverride = process.env["TWEAKCC_CC_INSTALLATION_PATH"]?.trim() || null;
14260
- const nativeSymlink = join7(homedir2(), ".local", "bin", "claude");
14439
+ const nativeSymlink = join8(homedir3(), ".local", "bin", "claude");
14261
14440
  if (envOverride && !existsSync4(envOverride)) {
14262
14441
  return { ok: false, reason: "patch-target-missing", declaredPath: envOverride };
14263
14442
  }
@@ -14265,7 +14444,7 @@ function resolveClaudeBinaryForPatch() {
14265
14444
  if (!source) return { ok: false, reason: "binary-not-found" };
14266
14445
  let resolved;
14267
14446
  try {
14268
- resolved = realpathSync2(source);
14447
+ resolved = realpathSync3(source);
14269
14448
  } catch {
14270
14449
  return { ok: false, reason: "binary-not-found" };
14271
14450
  }
@@ -14285,8 +14464,8 @@ function resolveClaudeBinaryForPatch() {
14285
14464
  let launchOverrideResolved = null;
14286
14465
  if (launchOverride) {
14287
14466
  try {
14288
- const followedOverride = resolveThroughNpmShims(realpathSync2(launchOverride));
14289
- launchOverrideResolved = followedOverride.ok ? followedOverride.path : realpathSync2(launchOverride);
14467
+ const followedOverride = resolveThroughNpmShims(realpathSync3(launchOverride));
14468
+ launchOverrideResolved = followedOverride.ok ? followedOverride.path : realpathSync3(launchOverride);
14290
14469
  } catch {
14291
14470
  launchOverrideResolved = launchOverride;
14292
14471
  }
@@ -14425,11 +14604,11 @@ async function applyPatch(binaryPath, version, desired, configHash, opts) {
14425
14604
  try {
14426
14605
  mkdirSync5(backupDir(), { recursive: true });
14427
14606
  const { tryDetectInstallation, readContent, writeContent } = await import("tweakcc");
14428
- candidateDir = mkdtempSync(join7(dirname5(binaryPath), ".clodex-patch-"));
14429
- const candidatePath = join7(candidateDir, basename(binaryPath));
14607
+ candidateDir = mkdtempSync(join8(dirname5(binaryPath), ".clodex-patch-"));
14608
+ const candidatePath = join8(candidateDir, basename(binaryPath));
14430
14609
  const seedCandidate = async (from) => {
14431
14610
  copyFileSync(from, candidatePath);
14432
- const shim = shimEntryModuleName(candidatePath);
14611
+ assertRecognizedEntryModule(candidatePath);
14433
14612
  const installation = await tryDetectInstallation({ path: candidatePath });
14434
14613
  const bundle = readClaudeBundle(candidatePath);
14435
14614
  if (!bundle && installation.kind === "native") {
@@ -14438,7 +14617,6 @@ async function applyPatch(binaryPath, version, desired, configHash, opts) {
14438
14617
  );
14439
14618
  }
14440
14619
  const source = bundle ? bundle.source : await readContent(installation);
14441
- if (shim) restoreEntryModuleName(candidatePath, shim, { resign: false });
14442
14620
  return { installation, source, bundle };
14443
14621
  };
14444
14622
  const facts = collectPristineFacts({
@@ -14548,8 +14726,6 @@ async function applyPatch(binaryPath, version, desired, configHash, opts) {
14548
14726
  local = discardLocalPatchOutcome(builtIn.content, local.results);
14549
14727
  }
14550
14728
  results = [...results, ...local.results];
14551
- const writeShim = shimEntryModuleName(candidatePath);
14552
- let publishedBlob = false;
14553
14729
  if (loaded.bundle) {
14554
14730
  const writable = writableModuleIndex(candidatePath);
14555
14731
  if (writable === null) {
@@ -14565,14 +14741,12 @@ async function applyPatch(binaryPath, version, desired, configHash, opts) {
14565
14741
  await writeContent(loaded.installation, plan2.content);
14566
14742
  if (bunPointerShim) restoreBunCompiledPointer(candidatePath, bunPointerShim);
14567
14743
  applyBundleWritePlan(candidatePath, plan2);
14568
- publishedBlob = true;
14569
14744
  } else {
14570
14745
  const bunPointerShim = shimBunCompiledPointer(candidatePath);
14571
14746
  await writeContent(loaded.installation, local.content);
14572
14747
  if (bunPointerShim) restoreBunCompiledPointer(candidatePath, bunPointerShim);
14573
14748
  }
14574
- if (writeShim) restoreEntryModuleName(candidatePath, writeShim, { resign: true });
14575
- else if (publishedBlob) resignMachOBinary(candidatePath);
14749
+ signAndVerifyMachOCandidate(candidatePath, binaryPath);
14576
14750
  patchedSize = statSync7(candidatePath).size;
14577
14751
  patchedSha256 = sha256File(candidatePath);
14578
14752
  renameSync2(candidatePath, binaryPath);
@@ -14713,6 +14887,9 @@ function runRestoreCommand(target) {
14713
14887
  p2.log.success(`Restored pristine claude ${version} from ${plan.backupPath}.`);
14714
14888
  return 0;
14715
14889
  }
14890
+ function warnOnEditorExtensionDrift(binaryPath, version) {
14891
+ for (const warning of claudeExtensionDriftWarnings({ binaryPath, version })) p2.log.warn(warning);
14892
+ }
14716
14893
  async function runPatchCommand(opts = {}) {
14717
14894
  const target = resolveClaudeBinaryForPatch();
14718
14895
  const ignoredOverride = takeIgnoredLaunchOverride();
@@ -14758,6 +14935,7 @@ async function runPatchCommand(opts = {}) {
14758
14935
  });
14759
14936
  if (state === "current") {
14760
14937
  p2.log.success(`claude ${version} is already patched with the current model config \u2014 nothing to do.`);
14938
+ warnOnEditorExtensionDrift(binaryPath, version);
14761
14939
  return 0;
14762
14940
  }
14763
14941
  const release = tryAcquirePatchLock();
@@ -14793,6 +14971,7 @@ async function runPatchCommand(opts = {}) {
14793
14971
  if (!opts.trace) {
14794
14972
  for (const line of outcome.detailLines ?? []) p2.log.info(pc3.dim(line));
14795
14973
  }
14974
+ warnOnEditorExtensionDrift(binaryPath, version);
14796
14975
  return 0;
14797
14976
  } finally {
14798
14977
  release();
@@ -15157,16 +15336,16 @@ function localProvidersToServerModels(localProviders) {
15157
15336
  }
15158
15337
 
15159
15338
  // src/registry/credential-cleanup-journal.ts
15160
- import { randomUUID as randomUUID7 } from "crypto";
15339
+ import { randomUUID as randomUUID8 } from "crypto";
15161
15340
  import {
15162
- closeSync as closeSync9,
15341
+ closeSync as closeSync10,
15163
15342
  existsSync as existsSync5,
15164
15343
  fstatSync as fstatSync3,
15165
15344
  fsyncSync as fsyncSync2,
15166
15345
  lstatSync,
15167
15346
  mkdirSync as mkdirSync6,
15168
- openSync as openSync9,
15169
- readFileSync as readFileSync10,
15347
+ openSync as openSync10,
15348
+ readFileSync as readFileSync11,
15170
15349
  renameSync as renameSync3,
15171
15350
  unlinkSync as unlinkSync4,
15172
15351
  writeFileSync as writeFileSync6
@@ -15257,7 +15436,7 @@ function readJournalUnlocked(path) {
15257
15436
  if (before.isSymbolicLink() || !before.isFile()) {
15258
15437
  throw new Error("Credential cleanup journal must be a regular file.");
15259
15438
  }
15260
- fd = openSync9(path, "r");
15439
+ fd = openSync10(path, "r");
15261
15440
  const opened = fstatSync3(fd);
15262
15441
  if (before.dev !== opened.dev || before.ino !== opened.ino) {
15263
15442
  throw new Error("Credential cleanup journal changed while opening.");
@@ -15273,44 +15452,44 @@ function readJournalUnlocked(path) {
15273
15452
  if (opened.size > MAX_JOURNAL_BYTES) {
15274
15453
  throw new Error("Credential cleanup journal is too large.");
15275
15454
  }
15276
- return parseJournal(JSON.parse(readFileSync10(fd, "utf8")));
15455
+ return parseJournal(JSON.parse(readFileSync11(fd, "utf8")));
15277
15456
  } catch (error) {
15278
15457
  const message = error instanceof Error ? error.message : String(error);
15279
15458
  throw new Error(`Could not read credential cleanup journal: ${message}`);
15280
15459
  } finally {
15281
- if (fd !== void 0) closeSync9(fd);
15460
+ if (fd !== void 0) closeSync10(fd);
15282
15461
  }
15283
15462
  }
15284
15463
  function syncParentDirectory(path) {
15285
15464
  let fd;
15286
15465
  try {
15287
- fd = openSync9(dirname6(path), "r");
15466
+ fd = openSync10(dirname6(path), "r");
15288
15467
  fsyncSync2(fd);
15289
15468
  } catch (error) {
15290
15469
  const code = error.code;
15291
15470
  if (code !== "EINVAL" && code !== "ENOTSUP" && code !== "EPERM") throw error;
15292
15471
  } finally {
15293
- if (fd !== void 0) closeSync9(fd);
15472
+ if (fd !== void 0) closeSync10(fd);
15294
15473
  }
15295
15474
  }
15296
15475
  function writeJournalUnlocked(journal, path) {
15297
15476
  assertRegistryWriteOwnership(path);
15298
15477
  ensureSecureAppHome();
15299
15478
  mkdirSync6(dirname6(path), { recursive: true, mode: DIR_MODE2 });
15300
- const tmp = `${path}.${process.pid}.${randomUUID7()}.tmp`;
15479
+ const tmp = `${path}.${process.pid}.${randomUUID8()}.tmp`;
15301
15480
  let fd;
15302
15481
  try {
15303
- fd = openSync9(tmp, "wx", FILE_MODE4);
15482
+ fd = openSync10(tmp, "wx", FILE_MODE4);
15304
15483
  writeFileSync6(fd, `${JSON.stringify(journal, null, 2)}
15305
15484
  `);
15306
15485
  fsyncSync2(fd);
15307
- closeSync9(fd);
15486
+ closeSync10(fd);
15308
15487
  fd = void 0;
15309
15488
  assertRegistryWriteOwnership(path);
15310
15489
  renameSync3(tmp, path);
15311
15490
  syncParentDirectory(path);
15312
15491
  } finally {
15313
- if (fd !== void 0) closeSync9(fd);
15492
+ if (fd !== void 0) closeSync10(fd);
15314
15493
  try {
15315
15494
  unlinkSync4(tmp);
15316
15495
  } catch (error) {
@@ -16340,7 +16519,8 @@ function readCapabilityFlags(m) {
16340
16519
  const bool = (v) => typeof v === "boolean" ? v : void 0;
16341
16520
  return {
16342
16521
  useResponsesLite: bool(m["use_responses_lite"]),
16343
- preferWebSockets: bool(m["prefer_websockets"])
16522
+ preferWebSockets: bool(m["prefer_websockets"]),
16523
+ minimalClientVersion: readCodexClientVersion(m["minimal_client_version"])
16344
16524
  };
16345
16525
  }
16346
16526
  function parseOpenAiModelEntries(body) {
@@ -16377,7 +16557,8 @@ function buildDynamicOAuthModel(entry, seedById, codexCatalog) {
16377
16557
  effectiveContextPercent: entry.effective_context_window_percent ?? seed.effectiveContextPercent,
16378
16558
  maxOutputTokens: entry.max_output_tokens ?? seed.maxOutputTokens,
16379
16559
  useResponsesLite: entry.useResponsesLite ?? seed.useResponsesLite,
16380
- preferWebSockets: entry.preferWebSockets ?? seed.preferWebSockets
16560
+ preferWebSockets: entry.preferWebSockets ?? seed.preferWebSockets,
16561
+ minimalClientVersion: entry.minimalClientVersion ?? seed.minimalClientVersion
16381
16562
  };
16382
16563
  }
16383
16564
  const { id } = entry;
@@ -16412,7 +16593,8 @@ function buildDynamicOAuthModel(entry, seedById, codexCatalog) {
16412
16593
  // so a wrong `true` here costs an unusable menu entry, not a 400.
16413
16594
  reasoning: codexCatalog ? true : modelPrefersResponsesApi(id),
16414
16595
  useResponsesLite: entry.useResponsesLite,
16415
- preferWebSockets: entry.preferWebSockets
16596
+ preferWebSockets: entry.preferWebSockets,
16597
+ minimalClientVersion: entry.minimalClientVersion
16416
16598
  };
16417
16599
  }
16418
16600
  async function fetchJsonWithAuth(url, accessToken, timeoutMs) {
@@ -16473,6 +16655,10 @@ async function refreshOpenAiOAuthModels(accessToken) {
16473
16655
  credentialRejected: credentialFailure !== void 0
16474
16656
  };
16475
16657
  }
16658
+ function warningSuffix(models) {
16659
+ const warning = codexClientVersionWarning(applyOAuthSeedContextMetadata(models));
16660
+ return warning ? ` ${warning}` : "";
16661
+ }
16476
16662
  async function refreshApiListProvider(provider, apiKey) {
16477
16663
  const npm = provider.api.npm ?? "@ai-sdk/openai-compatible";
16478
16664
  const retained = isRetainedOpenCodeGoProvider(provider);
@@ -16682,7 +16868,7 @@ async function refreshProviderModels(providerId, apiKey, registry, credentialSna
16682
16868
  if (oauthResult.source === "seed" && cachedModelCount(cacheProvider) > 0) {
16683
16869
  return skipWithCachedModels(
16684
16870
  cacheProvider,
16685
- `Live model discovery failed${failureDetail} \u2014 kept your existing cached model list instead of overwriting it with clodex's built-in fallback list. Try refreshing again later.`
16871
+ `Live model discovery failed${failureDetail} \u2014 kept your existing cached model list instead of overwriting it with clodex's built-in fallback list. Try refreshing again later.` + warningSuffix(cacheProvider.modelsCache.models)
16686
16872
  );
16687
16873
  }
16688
16874
  if (oauthResult.source === "seed") {
@@ -16776,7 +16962,7 @@ async function refreshProviderModels(providerId, apiKey, registry, credentialSna
16776
16962
  ok: true,
16777
16963
  modelCount: enriched.length,
16778
16964
  previousModelCount: hadPreviousRefresh ? previousModelCount : void 0,
16779
- reason: oauthFallbackReason
16965
+ reason: isChatGptOAuthProvider(provider) ? [oauthFallbackReason, codexClientVersionWarning(models)].filter(Boolean).join(" ") || void 0 : void 0
16780
16966
  };
16781
16967
  } catch (err) {
16782
16968
  return {
@@ -17133,6 +17319,7 @@ async function authenticateProvider(providerId, options = {}) {
17133
17319
  refreshSpinner.stop(`Models not refreshed${refreshResult.reason ? ` \u2014 ${refreshResult.reason}` : ""}`);
17134
17320
  } else if (refreshResult.ok) {
17135
17321
  refreshSpinner.stop("Models refreshed");
17322
+ if (refreshResult.reason) p4.log.warn(refreshResult.reason);
17136
17323
  } else {
17137
17324
  refreshSpinner.stop(`Could not refresh models${refreshResult.reason ? ` \u2014 ${refreshResult.reason}` : ""}`);
17138
17325
  }
@@ -18406,7 +18593,7 @@ async function askSaveServerPassword() {
18406
18593
 
18407
18594
  // src/server/router.ts
18408
18595
  import { createServer as createServer2 } from "http";
18409
- import { randomUUID as randomUUID8 } from "crypto";
18596
+ import { randomUUID as randomUUID9 } from "crypto";
18410
18597
 
18411
18598
  // src/openai-adapter.ts
18412
18599
  import { tool as tool2, jsonSchema as jsonSchema2, streamText as streamText2, generateText as generateText2 } from "ai";
@@ -18812,7 +18999,7 @@ async function routeRequest(req, res, options, modelCache, plog) {
18812
18999
  }
18813
19000
  async function handleAnthropicMessages(req, res, options, modelCache, plog) {
18814
19001
  const clientAbort = watchClientDisconnect(res);
18815
- const body = await readJson(req);
19002
+ const body = await readJson2(req);
18816
19003
  if (!body) {
18817
19004
  sendJson(res, 400, { error: { message: "Invalid JSON body" } });
18818
19005
  return;
@@ -18822,7 +19009,7 @@ async function handleAnthropicMessages(req, res, options, modelCache, plog) {
18822
19009
  plog(`model not found: ${body.model}`);
18823
19010
  return;
18824
19011
  }
18825
- const requestId = randomUUID8();
19012
+ const requestId = randomUUID9();
18826
19013
  const claudeSessionIdHeader = Array.isArray(req.headers["x-claude-code-session-id"]) ? req.headers["x-claude-code-session-id"][0] : req.headers["x-claude-code-session-id"];
18827
19014
  const claudeSessionId = extractClaudeSessionId(body, claudeSessionIdHeader);
18828
19015
  const claudeAgentIds = extractClaudeAgentIds(req.headers);
@@ -19082,7 +19269,7 @@ data: ${JSON.stringify({
19082
19269
  }
19083
19270
  async function handleAnthropicCountTokens(req, res, options, plog) {
19084
19271
  const clientAbort = watchClientDisconnect(res);
19085
- const body = await readJson(req);
19272
+ const body = await readJson2(req);
19086
19273
  if (!body) {
19087
19274
  sendJson(res, 400, { error: { message: "Invalid JSON body" } });
19088
19275
  return;
@@ -19151,7 +19338,7 @@ async function handleAnthropicCountTokens(req, res, options, plog) {
19151
19338
  }
19152
19339
  async function handleOpenAIChatCompletions(req, res, options, modelCache, plog) {
19153
19340
  const clientAbort = watchClientDisconnect(res);
19154
- const body = await readJson(req);
19341
+ const body = await readJson2(req);
19155
19342
  if (!body) {
19156
19343
  sendJson(res, 400, { error: { message: "Invalid JSON body" } });
19157
19344
  return;
@@ -19358,7 +19545,7 @@ function getResponseModelId(bodyModel, model, options) {
19358
19545
  if (typeof bodyModel === "string" && options.aliasNames?.has(bodyModel)) return bodyModel;
19359
19546
  return options.gateway?.maskGatewayIds ? gatewayDisplayName(model, options.gateway) : typeof bodyModel === "string" ? bodyModel : model.id;
19360
19547
  }
19361
- async function readJson(req) {
19548
+ async function readJson2(req) {
19362
19549
  try {
19363
19550
  const raw = await readBody(req);
19364
19551
  return raw ? JSON.parse(raw) : {};
@@ -19496,14 +19683,14 @@ import * as http2 from "http";
19496
19683
  import * as https from "https";
19497
19684
  import * as net from "net";
19498
19685
  import { PassThrough } from "stream";
19499
- import { randomUUID as randomUUID9 } from "crypto";
19686
+ import { randomUUID as randomUUID10 } from "crypto";
19500
19687
  import { URL as URL2 } from "url";
19501
19688
  import { createBrotliDecompress, createGunzip, createInflate } from "zlib";
19502
19689
 
19503
19690
  // src/http-proxy/ca.ts
19504
19691
  import { randomBytes as randomBytes2 } from "crypto";
19505
- import { chmodSync as chmodSync5, existsSync as existsSync6, mkdirSync as mkdirSync7, readFileSync as readFileSync11, writeFileSync as writeFileSync7 } from "fs";
19506
- import { dirname as dirname7, join as join8, resolve as resolve3 } from "path";
19692
+ import { chmodSync as chmodSync5, existsSync as existsSync6, mkdirSync as mkdirSync7, readFileSync as readFileSync12, writeFileSync as writeFileSync7 } from "fs";
19693
+ import { dirname as dirname7, join as join9, resolve as resolve3 } from "path";
19507
19694
  import forge from "node-forge";
19508
19695
  var CERT_DIR = "http-proxy";
19509
19696
  var CA_CERT_FILE = "clodex-ca.pem";
@@ -19518,14 +19705,14 @@ function serialNumber() {
19518
19705
  return bytes.toString("hex");
19519
19706
  }
19520
19707
  function certPaths() {
19521
- const dir = join8(getAppHome(), CERT_DIR);
19708
+ const dir = join9(getAppHome(), CERT_DIR);
19522
19709
  return {
19523
19710
  dir,
19524
- caCert: join8(dir, CA_CERT_FILE),
19525
- caKey: join8(dir, CA_KEY_FILE),
19526
- serverCert: join8(dir, SERVER_CERT_FILE),
19527
- serverKey: join8(dir, SERVER_KEY_FILE),
19528
- version: join8(dir, CERT_VERSION_FILE)
19711
+ caCert: join9(dir, CA_CERT_FILE),
19712
+ caKey: join9(dir, CA_KEY_FILE),
19713
+ serverCert: join9(dir, SERVER_CERT_FILE),
19714
+ serverKey: join9(dir, SERVER_KEY_FILE),
19715
+ version: join9(dir, CERT_VERSION_FILE)
19529
19716
  };
19530
19717
  }
19531
19718
  function writePrivate(path, value) {
@@ -19578,8 +19765,8 @@ function generateCertificates(paths) {
19578
19765
  }
19579
19766
  function storedCertificatesAreCurrent(paths) {
19580
19767
  try {
19581
- const ca = forge.pki.certificateFromPem(readFileSync11(paths.caCert, "utf8"));
19582
- const server = forge.pki.certificateFromPem(readFileSync11(paths.serverCert, "utf8"));
19768
+ const ca = forge.pki.certificateFromPem(readFileSync12(paths.caCert, "utf8"));
19769
+ const server = forge.pki.certificateFromPem(readFileSync12(paths.serverCert, "utf8"));
19583
19770
  const now = Date.now();
19584
19771
  const renewalBuffer = 7 * 24 * 60 * 60 * 1e3;
19585
19772
  return ca.validity.notBefore.getTime() <= now && ca.validity.notAfter.getTime() > now + renewalBuffer && server.validity.notBefore.getTime() <= now && server.validity.notAfter.getTime() > now + renewalBuffer && ca.verify(ca) && ca.verify(server);
@@ -19590,13 +19777,13 @@ function storedCertificatesAreCurrent(paths) {
19590
19777
  function ensureHttpProxyCertificates() {
19591
19778
  const paths = certPaths();
19592
19779
  const required = [paths.caCert, paths.caKey, paths.serverCert, paths.serverKey, paths.version];
19593
- const current = required.every(existsSync6) && readFileSync11(paths.version, "utf8") === CERT_VERSION && storedCertificatesAreCurrent(paths);
19780
+ const current = required.every(existsSync6) && readFileSync12(paths.version, "utf8") === CERT_VERSION && storedCertificatesAreCurrent(paths);
19594
19781
  if (!current) generateCertificates(paths);
19595
19782
  return {
19596
19783
  caCertPath: paths.caCert,
19597
- caCert: readFileSync11(paths.caCert, "utf8"),
19598
- serverCert: readFileSync11(paths.serverCert, "utf8"),
19599
- serverKey: readFileSync11(paths.serverKey, "utf8")
19784
+ caCert: readFileSync12(paths.caCert, "utf8"),
19785
+ serverCert: readFileSync12(paths.serverCert, "utf8"),
19786
+ serverKey: readFileSync12(paths.serverKey, "utf8")
19600
19787
  };
19601
19788
  }
19602
19789
  function ensureHttpProxyCaBundle(relayCaCertPath, additionalCaCertPath, onWarning) {
@@ -19610,7 +19797,7 @@ function ensureHttpProxyCaBundle(relayCaCertPath, additionalCaCertPath, onWarnin
19610
19797
  let additionalCa;
19611
19798
  try {
19612
19799
  if (resolve3(additionalCaCertPath) === resolve3(relayCaCertPath)) return relayCaCertPath;
19613
- additionalCa = readFileSync11(additionalCaCertPath, "utf8").trim();
19800
+ additionalCa = readFileSync12(additionalCaCertPath, "utf8").trim();
19614
19801
  } catch (err) {
19615
19802
  return warn(
19616
19803
  `NODE_EXTRA_CA_CERTS=${additionalCaCertPath} cannot be read (${err instanceof Error ? err.message : String(err)}), so it is not part of the proxy CA bundle. Where node reports this itself it says only "Ignoring extra certs ... load failed", without naming the variable. Clear or correct it.`
@@ -19620,8 +19807,8 @@ function ensureHttpProxyCaBundle(relayCaCertPath, additionalCaCertPath, onWarnin
19620
19807
  return warn(`NODE_EXTRA_CA_CERTS=${additionalCaCertPath} is empty, so it adds nothing.`);
19621
19808
  }
19622
19809
  try {
19623
- const relayCa = readFileSync11(relayCaCertPath, "utf8").trimEnd();
19624
- const combinedPath = join8(dirname7(relayCaCertPath), "combined-ca.pem");
19810
+ const relayCa = readFileSync12(relayCaCertPath, "utf8").trimEnd();
19811
+ const combinedPath = join9(dirname7(relayCaCertPath), "combined-ca.pem");
19625
19812
  writePublic(combinedPath, `${relayCa}
19626
19813
  ${additionalCa}
19627
19814
  `);
@@ -19788,7 +19975,7 @@ function requestHeadersWithoutProxyHeaders(req) {
19788
19975
  const headers = [];
19789
19976
  for (let i = 0; i < req.rawHeaders.length; i += 2) {
19790
19977
  const name = req.rawHeaders[i];
19791
- if (/^proxy-(authorization|connection)$/i.test(name)) continue;
19978
+ if (/^proxy-(authorization|connection)$/i.test(name) || name.toLowerCase() === OUTBOUND_PROXY_HOP_HEADER) continue;
19792
19979
  headers.push(name, req.rawHeaders[i + 1] ?? "");
19793
19980
  }
19794
19981
  return headers;
@@ -20325,6 +20512,7 @@ async function startHttpProxy(options) {
20325
20512
  const anthropicOrigin = new URL2(options.anthropicOrigin ?? "https://api.anthropic.com");
20326
20513
  const anthropicProxyUrl = outboundProxyUrlForTarget(anthropicOrigin.href);
20327
20514
  let anthropicAgent;
20515
+ let anthropicViaProxy = false;
20328
20516
  let adapter = options.adapterHandle ?? null;
20329
20517
  if (options.routes.length > 0) {
20330
20518
  adapter ??= await startProxyCatalog(
@@ -20339,11 +20527,27 @@ async function startHttpProxy(options) {
20339
20527
  }
20340
20528
  const adapterAgent = adapter ? new http2.Agent({ keepAlive: true }) : void 0;
20341
20529
  let shuttingDown = false;
20530
+ let warnedProxyLoop = false;
20531
+ const rejectProxyLoop = (target) => {
20532
+ if (warnedProxyLoop) return;
20533
+ warnedProxyLoop = true;
20534
+ const secure = target.startsWith("https:");
20535
+ const candidates = secure ? ["HTTPS_PROXY", "https_proxy"] : ["HTTP_PROXY", "http_proxy"];
20536
+ const name = candidates.find((key) => process.env[key]?.trim()) ?? candidates[0];
20537
+ emitParentNotice(`clodex: outbound proxy loop detected; check ${name} (request returned to this proxy)`);
20538
+ };
20342
20539
  const mitmServer = https.createServer({
20343
20540
  key: certificates.serverKey,
20344
20541
  cert: certificates.serverCert,
20345
20542
  minVersion: "TLSv1.2"
20346
20543
  }, async (req, res) => {
20544
+ if (isOwnOutboundProxyHop(req.headers[OUTBOUND_PROXY_HOP_HEADER])) {
20545
+ rejectProxyLoop("https://api.anthropic.com");
20546
+ res.writeHead(508, { "Content-Type": "text/plain" });
20547
+ res.end("Outbound proxy loop detected");
20548
+ return;
20549
+ }
20550
+ delete req.headers[OUTBOUND_PROXY_HOP_HEADER];
20347
20551
  let rawBody;
20348
20552
  try {
20349
20553
  rawBody = await readRawBody(req);
@@ -20354,7 +20558,7 @@ async function startHttpProxy(options) {
20354
20558
  }
20355
20559
  const messagesEndpoint = anthropicMessagesEndpoint(req.url);
20356
20560
  if (req.method === "POST" && messagesEndpoint) {
20357
- const requestId = randomUUID9();
20561
+ const requestId = randomUUID10();
20358
20562
  let parsed = null;
20359
20563
  let route;
20360
20564
  let adapterBody = rawBody;
@@ -20502,6 +20706,12 @@ async function startHttpProxy(options) {
20502
20706
  return connectTunnelAgents.get(proxyUrl);
20503
20707
  };
20504
20708
  mitmServer.on("upgrade", (req, socket, head) => {
20709
+ if (isOwnOutboundProxyHop(req.headers[OUTBOUND_PROXY_HOP_HEADER])) {
20710
+ rejectProxyLoop("https://api.anthropic.com");
20711
+ socket.once("error", () => socket.destroy());
20712
+ socket.end("HTTP/1.1 508 Loop Detected\r\nConnection: close\r\n\r\n", () => socket.destroy());
20713
+ return;
20714
+ }
20505
20715
  forwardAnthropicUpgrade(
20506
20716
  req,
20507
20717
  socket,
@@ -20512,13 +20722,40 @@ async function startHttpProxy(options) {
20512
20722
  sockets
20513
20723
  );
20514
20724
  });
20515
- const proxyServer = http2.createServer(forwardPlainHttp);
20725
+ const proxyServer = http2.createServer((req, res) => {
20726
+ if (isOwnOutboundProxyHop(req.headers[OUTBOUND_PROXY_HOP_HEADER])) {
20727
+ rejectProxyLoop(req.url ?? "");
20728
+ res.writeHead(508, { "Content-Type": "text/plain" });
20729
+ res.end("Outbound proxy loop detected");
20730
+ return;
20731
+ }
20732
+ forwardPlainHttp(req, res);
20733
+ });
20516
20734
  proxyServer.on("connection", (socket) => {
20517
20735
  sockets.add(socket);
20518
20736
  socket.once("close", () => sockets.delete(socket));
20519
20737
  });
20520
20738
  proxyServer.on("connect", (req, clientSocket, head) => {
20739
+ const ownHop = isOwnOutboundProxyHop(req.headers[OUTBOUND_PROXY_HOP_HEADER]);
20740
+ const rejectOwnHop = () => {
20741
+ rejectProxyLoop(`https://${req.url ?? ""}`);
20742
+ clientSocket.once("error", () => clientSocket.destroy());
20743
+ const body = "Outbound proxy loop detected";
20744
+ clientSocket.end(
20745
+ `HTTP/1.1 508 Loop Detected\r
20746
+ Content-Type: text/plain\r
20747
+ Content-Length: ${Buffer.byteLength(body)}\r
20748
+ Connection: close\r
20749
+ \r
20750
+ ${body}`,
20751
+ () => clientSocket.destroy()
20752
+ );
20753
+ };
20521
20754
  if (shouldInterceptConnect(req.url ?? "")) {
20755
+ if (ownHop && anthropicViaProxy) {
20756
+ rejectOwnHop();
20757
+ return;
20758
+ }
20522
20759
  clientSocket.write("HTTP/1.1 200 Connection Established\r\n\r\n");
20523
20760
  if (head.length > 0) clientSocket.unshift(head);
20524
20761
  mitmServer.emit("connection", clientSocket);
@@ -20541,6 +20778,10 @@ async function startHttpProxy(options) {
20541
20778
  );
20542
20779
  }
20543
20780
  const outboundAgent = outboundProxyUrl !== void 0 && !selfTargeting ? connectTunnelAgent(outboundProxyUrl, targetUrl) : void 0;
20781
+ if (ownHop && outboundAgent) {
20782
+ rejectOwnHop();
20783
+ return;
20784
+ }
20544
20785
  if (outboundAgent) {
20545
20786
  let upstream2;
20546
20787
  let proxyConnectStatus;
@@ -20559,7 +20800,7 @@ async function startHttpProxy(options) {
20559
20800
  if (proxyConnectStatus !== 200 || clientSocket.destroyed) {
20560
20801
  connected.destroy();
20561
20802
  if (!clientSocket.destroyed) {
20562
- clientSocket.end("HTTP/1.1 502 Bad Gateway\r\n\r\n", () => clientSocket.destroy());
20803
+ clientSocket.end(proxyConnectStatus === 508 ? "HTTP/1.1 508 Loop Detected\r\n\r\n" : "HTTP/1.1 502 Bad Gateway\r\n\r\n", () => clientSocket.destroy());
20563
20804
  }
20564
20805
  return;
20565
20806
  }
@@ -20637,6 +20878,7 @@ async function startHttpProxy(options) {
20637
20878
  );
20638
20879
  } else {
20639
20880
  anthropicAgent = outboundHttpProxyAgent(anthropicOrigin.href);
20881
+ anthropicViaProxy = anthropicAgent !== void 0;
20640
20882
  }
20641
20883
  anthropicAgent ??= createPassthroughAgent();
20642
20884
  return {
@@ -21502,9 +21744,9 @@ function planLaunchWizard(opts) {
21502
21744
 
21503
21745
  // src/vscode-launcher.ts
21504
21746
  import { spawnSync as spawnSync2 } from "child_process";
21505
- import { existsSync as existsSync7, mkdirSync as mkdirSync8, mkdtempSync as mkdtempSync2, readdirSync, readFileSync as readFileSync12, rmSync as rmSync2, statSync as statSync8, writeFileSync as writeFileSync8 } from "fs";
21506
- import { tmpdir } from "os";
21507
- import { dirname as dirname8, join as join9 } from "path";
21747
+ import { existsSync as existsSync7, mkdirSync as mkdirSync8, mkdtempSync as mkdtempSync2, readdirSync as readdirSync2, readFileSync as readFileSync13, rmSync as rmSync2, statSync as statSync8, writeFileSync as writeFileSync8 } from "fs";
21748
+ import { homedir as homedir4, tmpdir } from "os";
21749
+ import { dirname as dirname8, join as join10 } from "path";
21508
21750
  import { fileURLToPath } from "url";
21509
21751
  var LAUNCHER_EXE_NAME = "clodex-claude.exe";
21510
21752
  var VSCODE_WRAPPER_SETTING = "claudeCode.claudeProcessWrapper";
@@ -21513,15 +21755,15 @@ var WRAPPER_PATH_PLACEHOLDER = '"__CLODEX_WRAPPER_SCRIPT_PATH__"';
21513
21755
  var LAUNCHER_SOURCE_FILE = "clodex-claude-launcher.cs";
21514
21756
  var UTF8_BOM = "\uFEFF";
21515
21757
  var CSC_ARGS = ["/nologo", "/target:exe", "/platform:anycpu", "/optimize+", `/out:${LAUNCHER_EXE_NAME}`, LAUNCHER_SOURCE_FILE];
21516
- var packageRoot = join9(dirname8(fileURLToPath(import.meta.url)), "..");
21758
+ var packageRoot = join10(dirname8(fileURLToPath(import.meta.url)), "..");
21517
21759
  function defaultLauncherInputs() {
21518
21760
  return {
21519
- launcherSourcePath: join9(packageRoot, "launcher", LAUNCHER_SOURCE_FILE),
21520
- wrapperScriptPath: join9(packageRoot, "dist", "claude-wrapper.js")
21761
+ launcherSourcePath: join10(packageRoot, "launcher", LAUNCHER_SOURCE_FILE),
21762
+ wrapperScriptPath: join10(packageRoot, "dist", "claude-wrapper.js")
21521
21763
  };
21522
21764
  }
21523
21765
  function launcherInstallPath(env = process.env) {
21524
- return join9(getAppHome(env), "bin", LAUNCHER_EXE_NAME);
21766
+ return join10(getAppHome(env), "bin", LAUNCHER_EXE_NAME);
21525
21767
  }
21526
21768
  function csharpStringLiteral(value) {
21527
21769
  let literal = '"';
@@ -21547,30 +21789,23 @@ function renderLauncherSource(template, paths) {
21547
21789
  }
21548
21790
  function cscSearchRoots(env = process.env) {
21549
21791
  const windir = env.WINDIR?.trim() || env.SystemRoot?.trim() || "C:\\Windows";
21550
- const dotnet = join9(windir, "Microsoft.NET");
21551
- return [join9(dotnet, "Framework64"), join9(dotnet, "Framework")];
21792
+ const dotnet = join10(windir, "Microsoft.NET");
21793
+ return [join10(dotnet, "Framework64"), join10(dotnet, "Framework")];
21552
21794
  }
21553
21795
  function frameworkVersionKey(name) {
21554
21796
  return name.slice(1).split(".").map((part) => Number.parseInt(part, 10) || 0);
21555
21797
  }
21556
- function compareVersionKeys(a, b) {
21557
- for (let i = 0; i < Math.max(a.length, b.length); i += 1) {
21558
- const diff = (a[i] ?? 0) - (b[i] ?? 0);
21559
- if (diff !== 0) return diff;
21560
- }
21561
- return 0;
21562
- }
21563
21798
  function findCsc(env = process.env) {
21564
21799
  for (const root of cscSearchRoots(env)) {
21565
21800
  let entries;
21566
21801
  try {
21567
- entries = readdirSync(root);
21802
+ entries = readdirSync2(root);
21568
21803
  } catch {
21569
21804
  continue;
21570
21805
  }
21571
21806
  const candidates = entries.filter((name) => /^v4\.\d+(\.\d+)*$/i.test(name)).sort((a, b) => compareVersionKeys(frameworkVersionKey(b), frameworkVersionKey(a)));
21572
21807
  for (const version of candidates) {
21573
- const csc = join9(root, version, "csc.exe");
21808
+ const csc = join10(root, version, "csc.exe");
21574
21809
  try {
21575
21810
  if (statSync8(csc).isFile()) return csc;
21576
21811
  } catch {
@@ -21629,7 +21864,7 @@ function installVscodeLauncher(opts = {}) {
21629
21864
  }
21630
21865
  let source;
21631
21866
  try {
21632
- source = renderLauncherSource(readFileSync12(launcherSourcePath, "utf8"), { nodePath, wrapperScriptPath });
21867
+ source = renderLauncherSource(readFileSync13(launcherSourcePath, "utf8"), { nodePath, wrapperScriptPath });
21633
21868
  } catch (err) {
21634
21869
  return { ok: false, reason: "missing-input", message: `could not prepare the launcher source: ${describeError(err)}` };
21635
21870
  }
@@ -21637,7 +21872,7 @@ function installVscodeLauncher(opts = {}) {
21637
21872
  const tempRoot = opts.tempRoot ?? tmpdir();
21638
21873
  let scratch;
21639
21874
  try {
21640
- scratch = mkdtempSync2(join9(tempRoot, "clodex-vscode-launcher-"));
21875
+ scratch = mkdtempSync2(join10(tempRoot, "clodex-vscode-launcher-"));
21641
21876
  } catch (err) {
21642
21877
  return { ok: false, reason: "install-failed", message: `could not create a scratch directory under ${tempRoot}: ${describeError(err)}` };
21643
21878
  }
@@ -21652,9 +21887,9 @@ function installVscodeLauncher(opts = {}) {
21652
21887
  }
21653
21888
  function buildAndPublish(step) {
21654
21889
  const { scratch, cscPath, exePath } = step;
21655
- writeFileSync8(join9(scratch, LAUNCHER_SOURCE_FILE), UTF8_BOM + step.source, "utf8");
21890
+ writeFileSync8(join10(scratch, LAUNCHER_SOURCE_FILE), UTF8_BOM + step.source, "utf8");
21656
21891
  const compiled = step.compile(cscPath, CSC_ARGS, scratch);
21657
- const builtExe = join9(scratch, LAUNCHER_EXE_NAME);
21892
+ const builtExe = join10(scratch, LAUNCHER_EXE_NAME);
21658
21893
  if (compiled.error || compiled.status !== 0 || !existsSync7(builtExe)) {
21659
21894
  const output = [compiled.stdout, compiled.stderr].map((text6) => text6.trim()).filter(Boolean).join("\n");
21660
21895
  return {
@@ -21709,16 +21944,27 @@ function successReport(result) {
21709
21944
  "install: the paths above are compiled into the executable."
21710
21945
  ];
21711
21946
  }
21947
+ function launcherExtensionDriftWarnings(opts = {}) {
21948
+ const env = opts.env ?? process.env;
21949
+ const manifest = readPatchManifest(getPatchManifestPath(env));
21950
+ if (!manifest || typeof manifest.claudeVersion !== "string") return [];
21951
+ return claudeExtensionDriftWarnings(
21952
+ { binaryPath: manifest.binaryPath, version: manifest.claudeVersion },
21953
+ opts.homeDir ?? homedir4(),
21954
+ () => readNpmGlobalRoot(env)
21955
+ );
21956
+ }
21712
21957
  function runInstallVscodeLauncherCommand(opts = {}) {
21713
21958
  const result = installVscodeLauncher(opts);
21714
21959
  if (result.warning) console.error(`clodex: warning: ${result.warning}`);
21715
21960
  if (!result.ok) {
21716
21961
  console.error(`clodex: ${result.message}`);
21717
21962
  if (result.detail) console.error(result.detail);
21718
- return 1;
21963
+ } else {
21964
+ console.log(successReport(result).join("\n"));
21719
21965
  }
21720
- console.log(successReport(result).join("\n"));
21721
- return 0;
21966
+ for (const drift of launcherExtensionDriftWarnings(opts)) console.error(`clodex: warning: ${drift}`);
21967
+ return result.ok ? 0 : 1;
21722
21968
  }
21723
21969
 
21724
21970
  // src/cli.ts
@@ -22052,8 +22298,10 @@ ${pc13.bold("Options:")}
22052
22298
  --trace Write debug logs to ~/.clodex/logs/ and show errors on exit
22053
22299
  --fast Request Codex fast mode (service_tier=priority) on ChatGPT-OAuth models
22054
22300
  (equivalent to CLODEX_SERVICE_TIER=fast; warns if the SDK omits it)
22055
- --provider Boot provider id (skip wizard when paired with --model or in print mode)
22056
- --model Boot model id (skip wizard when paired with --provider or in print mode)
22301
+ --provider Endpoint mode only: boot provider id (skip wizard when paired with
22302
+ --model or in print mode)
22303
+ --model Endpoint mode only: boot model id (skip wizard when paired with
22304
+ --provider or in print mode)
22057
22305
  --context <model=stop> use a different share of a model's window for this
22058
22306
  launch only (standard, max, default, or a token count). Nothing is
22059
22307
  saved. It reaches Claude Code through the exported catalog, so a
@@ -22094,7 +22342,7 @@ ${pc13.bold("Examples:")}
22094
22342
  clodex claude --trace --resume abc-123
22095
22343
  clodex claude --endpoint
22096
22344
  clodex claude --endpoint --save-mode
22097
- clodex claude --provider openai-oauth --model gpt-6-sol
22345
+ clodex claude --endpoint --provider openai-oauth --model gpt-6-sol
22098
22346
  clodex claude -- --print "hello"
22099
22347
  clodex claude -- --dangerously-skip-permissions`;
22100
22348
  }
@@ -22216,8 +22464,11 @@ ${pc13.bold("How it works:")}
22216
22464
 
22217
22465
  ${pc13.bold("Context stops and cost:")}
22218
22466
  Some providers price large prompts in bands: GPT-5.6 and GPT-6 bill the full request at
22219
- a higher rate above 272,000 input tokens. The standard stop stays under that
22220
- line, so max is opt-in and reports the boundary when you select it.
22467
+ a higher rate above 272,000 input tokens. For ChatGPT/Codex-plan models whose
22468
+ catalog reports a 272,000-token window, standard stays at that line and a
22469
+ larger window is opt-in. OpenAI API-key models retain their full window, so
22470
+ standard may already cross the boundary. Choose a smaller numeric stop such
22471
+ as 250k to limit exposure to the higher rate.
22221
22472
 
22222
22473
  ${pc13.bold("Examples:")}
22223
22474
  clodex favorites
@@ -22254,7 +22505,13 @@ ${pc13.bold("Behavior:")}
22254
22505
  Local patches execute after the built-ins as an all-or-none set. Enabling
22255
22506
  them executes trusted JavaScript from local-patches.mjs with your full user
22256
22507
  permissions. Local failures are reported but never block the built-ins.
22257
- Run clodex patch again after every claude update.`;
22508
+ Run clodex patch again after every claude update.
22509
+
22510
+ An editor launching Claude Code through clodex-claude runs this patched
22511
+ install only when its Claude Code extension is the same build. When an
22512
+ installed extension is a different version, clodex patch warns and prints the
22513
+ commands that align them. Editors checked:
22514
+ ${CHECKED_EDITORS_DESCRIPTION}`;
22258
22515
  }
22259
22516
  function installVscodeLauncherHelpText() {
22260
22517
  return `${pc13.bold("clodex install-vscode-launcher")} v${VERSION}
@@ -22284,6 +22541,8 @@ ${pc13.bold("Behavior:")}
22284
22541
  As on macOS and Linux, clodex-claude then runs your clodex-patched install in
22285
22542
  place of the bundled claude.exe when the two builds match, so clodex models
22286
22543
  appear in the extension's model picker; re-run clodex patch after updates.
22544
+ Like clodex patch, it warns when an installed Claude Code extension is a
22545
+ different version from the Claude Code clodex last patched.
22287
22546
 
22288
22547
  The paths to node.exe and to the clodex install are compiled in: re-run this
22289
22548
  command after switching Node versions or moving the clodex install.
@@ -23234,7 +23493,7 @@ Error: ${resolved.error}
23234
23493
  function isCliEntryPoint() {
23235
23494
  if (!process.argv[1]) return false;
23236
23495
  try {
23237
- return realpathSync3(fileURLToPath2(import.meta.url)) === realpathSync3(process.argv[1]);
23496
+ return realpathSync4(fileURLToPath2(import.meta.url)) === realpathSync4(process.argv[1]);
23238
23497
  } catch {
23239
23498
  return false;
23240
23499
  }