@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/README.md +27 -8
- package/dist/{chunk-5TG736TX.js → chunk-YGEPQOPA.js} +3 -3
- package/dist/chunk-YGEPQOPA.js.map +1 -0
- package/dist/claude-wrapper.js +1 -1
- package/dist/cli.js +705 -446
- package/dist/cli.js.map +1 -1
- package/docs/background-agents.md +3 -1
- package/docs/windows-setup.md +27 -4
- package/package.json +2 -3
- package/dist/chunk-5TG736TX.js.map +0 -1
package/dist/cli.js
CHANGED
|
@@ -74,7 +74,7 @@ import {
|
|
|
74
74
|
withRegistryWriteLockSync,
|
|
75
75
|
writeParentNoticeLines,
|
|
76
76
|
writeParentNoticeLinesSync
|
|
77
|
-
} from "./chunk-
|
|
77
|
+
} from "./chunk-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
|
|
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.
|
|
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.
|
|
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}` : "") + "
|
|
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
|
|
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
|
|
3877
|
-
closeSync as
|
|
3878
|
-
realpathSync as
|
|
3875
|
+
openSync as openSync9,
|
|
3876
|
+
closeSync as closeSync9,
|
|
3877
|
+
realpathSync as realpathSync3
|
|
3879
3878
|
} from "fs";
|
|
3880
|
-
import { homedir as
|
|
3881
|
-
import { basename, dirname as dirname5, join as
|
|
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))
|
|
6811
|
-
|
|
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
|
|
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
|
|
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 =
|
|
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
|
|
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
|
|
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 =
|
|
7105
|
-
const installScriptPath =
|
|
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(
|
|
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/
|
|
7161
|
-
import {
|
|
7162
|
-
import {
|
|
7163
|
-
|
|
7164
|
-
|
|
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
|
|
7349
|
-
if (
|
|
7350
|
-
|
|
7351
|
-
|
|
7352
|
-
|
|
7353
|
-
|
|
7354
|
-
|
|
7355
|
-
|
|
7356
|
-
|
|
7357
|
-
|
|
7358
|
-
|
|
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)
|
|
7408
|
-
}
|
|
7409
|
-
function resignMachOBinary(path) {
|
|
7410
|
-
const fd = openSync3(path, "r");
|
|
7411
|
-
let machO;
|
|
7405
|
+
if (!machO) return;
|
|
7412
7406
|
try {
|
|
7413
|
-
|
|
7414
|
-
|
|
7415
|
-
|
|
7416
|
-
|
|
7417
|
-
|
|
7418
|
-
|
|
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
|
|
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
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 (
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
7627
|
-
|
|
7628
|
-
if (!
|
|
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 (!
|
|
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
|
|
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 =
|
|
7924
|
+
const fd = openSync6(path, "r");
|
|
7757
7925
|
try {
|
|
7758
7926
|
let read = 0;
|
|
7759
7927
|
while (read < table.byteCount) {
|
|
7760
|
-
const got =
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
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 =
|
|
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
|
|
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) =>
|
|
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
|
-
|
|
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
|
|
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 =
|
|
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
|
|
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.
|
|
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
|
|
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_" +
|
|
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
|
|
12159
|
+
import { randomUUID as randomUUID7 } from "crypto";
|
|
11976
12160
|
|
|
11977
12161
|
// src/sdk-adapter.ts
|
|
11978
|
-
import { createHash as createHash7, randomUUID as
|
|
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
|
-
|
|
12519
|
-
|
|
12520
|
-
|
|
12521
|
-
|
|
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_" +
|
|
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 =
|
|
13531
|
+
const fd = openSync8(logPath, "a", 384);
|
|
13353
13532
|
try {
|
|
13354
|
-
|
|
13533
|
+
writeSync3(fd, `${(/* @__PURE__ */ new Date()).toISOString()} ${redacted}
|
|
13355
13534
|
`);
|
|
13356
13535
|
} finally {
|
|
13357
|
-
|
|
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 =
|
|
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 ??
|
|
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 ??
|
|
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
|
|
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(
|
|
14401
|
+
mkdirSync5(join8(lockPath, ".."), { recursive: true, mode: 448 });
|
|
14223
14402
|
for (let attempt = 0; attempt < 2; attempt++) {
|
|
14224
14403
|
try {
|
|
14225
|
-
const fd =
|
|
14404
|
+
const fd = openSync9(lockPath, "wx");
|
|
14226
14405
|
const content = { pid: process.pid, startedAt: now };
|
|
14227
14406
|
writeFileSync5(fd, JSON.stringify(content));
|
|
14228
|
-
|
|
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(
|
|
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 =
|
|
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 =
|
|
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(
|
|
14289
|
-
launchOverrideResolved = followedOverride.ok ? followedOverride.path :
|
|
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(
|
|
14429
|
-
const candidatePath =
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
15339
|
+
import { randomUUID as randomUUID8 } from "crypto";
|
|
15161
15340
|
import {
|
|
15162
|
-
closeSync as
|
|
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
|
|
15169
|
-
readFileSync as
|
|
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 =
|
|
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(
|
|
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)
|
|
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 =
|
|
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)
|
|
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}.${
|
|
15479
|
+
const tmp = `${path}.${process.pid}.${randomUUID8()}.tmp`;
|
|
15301
15480
|
let fd;
|
|
15302
15481
|
try {
|
|
15303
|
-
fd =
|
|
15482
|
+
fd = openSync10(tmp, "wx", FILE_MODE4);
|
|
15304
15483
|
writeFileSync6(fd, `${JSON.stringify(journal, null, 2)}
|
|
15305
15484
|
`);
|
|
15306
15485
|
fsyncSync2(fd);
|
|
15307
|
-
|
|
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)
|
|
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
|
|
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
|
|
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 =
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
19506
|
-
import { dirname as dirname7, join as
|
|
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 =
|
|
19708
|
+
const dir = join9(getAppHome(), CERT_DIR);
|
|
19522
19709
|
return {
|
|
19523
19710
|
dir,
|
|
19524
|
-
caCert:
|
|
19525
|
-
caKey:
|
|
19526
|
-
serverCert:
|
|
19527
|
-
serverKey:
|
|
19528
|
-
version:
|
|
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(
|
|
19582
|
-
const server = forge.pki.certificateFromPem(
|
|
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) &&
|
|
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:
|
|
19598
|
-
serverCert:
|
|
19599
|
-
serverKey:
|
|
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 =
|
|
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 =
|
|
19624
|
-
const combinedPath =
|
|
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 =
|
|
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(
|
|
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
|
|
21506
|
-
import { tmpdir } from "os";
|
|
21507
|
-
import { dirname as dirname8, join as
|
|
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 =
|
|
21758
|
+
var packageRoot = join10(dirname8(fileURLToPath(import.meta.url)), "..");
|
|
21517
21759
|
function defaultLauncherInputs() {
|
|
21518
21760
|
return {
|
|
21519
|
-
launcherSourcePath:
|
|
21520
|
-
wrapperScriptPath:
|
|
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
|
|
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 =
|
|
21551
|
-
return [
|
|
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 =
|
|
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 =
|
|
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(
|
|
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(
|
|
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(
|
|
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 =
|
|
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
|
-
|
|
21963
|
+
} else {
|
|
21964
|
+
console.log(successReport(result).join("\n"));
|
|
21719
21965
|
}
|
|
21720
|
-
|
|
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
|
|
22056
|
-
|
|
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.
|
|
22220
|
-
|
|
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
|
|
23496
|
+
return realpathSync4(fileURLToPath2(import.meta.url)) === realpathSync4(process.argv[1]);
|
|
23238
23497
|
} catch {
|
|
23239
23498
|
return false;
|
|
23240
23499
|
}
|