@bman654/clodex 2.11.5 → 2.11.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -0
- package/dist/cli.js +380 -134
- package/dist/cli.js.map +1 -1
- package/docs/windows-setup.md +300 -0
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -382,7 +382,7 @@ import { join } from "path";
|
|
|
382
382
|
// package.json
|
|
383
383
|
var package_default = {
|
|
384
384
|
name: "@bman654/clodex",
|
|
385
|
-
version: "2.11.
|
|
385
|
+
version: "2.11.7",
|
|
386
386
|
publishConfig: {
|
|
387
387
|
access: "public"
|
|
388
388
|
},
|
|
@@ -3888,18 +3888,18 @@ import {
|
|
|
3888
3888
|
existsSync as existsSync5,
|
|
3889
3889
|
mkdtempSync,
|
|
3890
3890
|
mkdirSync as mkdirSync5,
|
|
3891
|
-
readFileSync as
|
|
3891
|
+
readFileSync as readFileSync10,
|
|
3892
3892
|
renameSync as renameSync2,
|
|
3893
3893
|
rmSync,
|
|
3894
|
-
statSync as
|
|
3894
|
+
statSync as statSync8,
|
|
3895
3895
|
unlinkSync as unlinkSync3,
|
|
3896
3896
|
writeFileSync as writeFileSync5,
|
|
3897
|
-
openSync as
|
|
3898
|
-
closeSync as
|
|
3897
|
+
openSync as openSync7,
|
|
3898
|
+
closeSync as closeSync7,
|
|
3899
3899
|
realpathSync as realpathSync2
|
|
3900
3900
|
} from "fs";
|
|
3901
3901
|
import { homedir as homedir3 } from "os";
|
|
3902
|
-
import { basename, dirname as
|
|
3902
|
+
import { basename, dirname as dirname5, join as join8 } from "path";
|
|
3903
3903
|
import pc3 from "picocolors";
|
|
3904
3904
|
import * as p2 from "@clack/prompts";
|
|
3905
3905
|
|
|
@@ -6974,8 +6974,103 @@ function resolveThroughNpmShims(startPath) {
|
|
|
6974
6974
|
};
|
|
6975
6975
|
}
|
|
6976
6976
|
|
|
6977
|
+
// src/claude-native-placeholder.ts
|
|
6978
|
+
import { spawnSync } from "child_process";
|
|
6979
|
+
import {
|
|
6980
|
+
closeSync as closeSync2,
|
|
6981
|
+
fstatSync,
|
|
6982
|
+
openSync as openSync2,
|
|
6983
|
+
readFileSync as readFileSync8,
|
|
6984
|
+
readSync,
|
|
6985
|
+
statSync as statSync5
|
|
6986
|
+
} from "fs";
|
|
6987
|
+
import { createRequire } from "module";
|
|
6988
|
+
import { arch } from "os";
|
|
6989
|
+
import { dirname as dirname4, join as join6 } from "path";
|
|
6990
|
+
var MAX_PLACEHOLDER_BYTES = 64 * 1024;
|
|
6991
|
+
var CLAUDE_PACKAGE = "@anthropic-ai/claude-code";
|
|
6992
|
+
var MISSING_BINARY_MARKER = "claude native binary not installed";
|
|
6993
|
+
var INSTALL_SCRIPT_MARKER = "node_modules/@anthropic-ai/claude-code/install.cjs";
|
|
6994
|
+
var OMIT_OPTIONAL_MARKER = "--omit=optional";
|
|
6995
|
+
var IGNORE_SCRIPTS_MARKER = "--ignore-scripts";
|
|
6996
|
+
function detectMusl() {
|
|
6997
|
+
if (process.platform !== "linux") return false;
|
|
6998
|
+
const report = typeof process.report?.getReport === "function" ? process.report.getReport() : null;
|
|
6999
|
+
return report != null && report.header?.glibcVersionRuntime === void 0;
|
|
7000
|
+
}
|
|
7001
|
+
function getPlatformKey() {
|
|
7002
|
+
const platform = process.platform;
|
|
7003
|
+
let cpu = arch();
|
|
7004
|
+
if (platform === "android") return `linux-${cpu}-android`;
|
|
7005
|
+
if (platform === "linux") return `linux-${cpu}${detectMusl() ? "-musl" : ""}`;
|
|
7006
|
+
if (platform === "darwin" && cpu === "x64") {
|
|
7007
|
+
const translated = spawnSync("sysctl", ["-n", "sysctl.proc_translated"], {
|
|
7008
|
+
encoding: "utf8"
|
|
7009
|
+
});
|
|
7010
|
+
if (translated.stdout?.trim() === "1") cpu = "arm64";
|
|
7011
|
+
}
|
|
7012
|
+
return `${platform}-${cpu}`;
|
|
7013
|
+
}
|
|
7014
|
+
function inspectNativePackage(binaryPath) {
|
|
7015
|
+
const packageRoot = dirname4(dirname4(binaryPath));
|
|
7016
|
+
const packageJsonPath = join6(packageRoot, "package.json");
|
|
7017
|
+
const installScriptPath = join6(packageRoot, "install.cjs");
|
|
7018
|
+
let manifest;
|
|
7019
|
+
try {
|
|
7020
|
+
manifest = JSON.parse(readFileSync8(packageJsonPath, "utf8"));
|
|
7021
|
+
if (manifest.name !== CLAUDE_PACKAGE || !statSync5(installScriptPath).isFile()) {
|
|
7022
|
+
return { nativePackageState: "unknown", installScriptPath: null };
|
|
7023
|
+
}
|
|
7024
|
+
} catch {
|
|
7025
|
+
return { nativePackageState: "unknown", installScriptPath: null };
|
|
7026
|
+
}
|
|
7027
|
+
const nativePackage = `${CLAUDE_PACKAGE}-${getPlatformKey()}`;
|
|
7028
|
+
if (manifest.optionalDependencies === null || typeof manifest.optionalDependencies !== "object" || typeof manifest.optionalDependencies[nativePackage] !== "string") {
|
|
7029
|
+
return { nativePackageState: "unknown", installScriptPath };
|
|
7030
|
+
}
|
|
7031
|
+
try {
|
|
7032
|
+
createRequire(installScriptPath).resolve(`${nativePackage}/package.json`);
|
|
7033
|
+
return { nativePackageState: "present", installScriptPath };
|
|
7034
|
+
} catch {
|
|
7035
|
+
return { nativePackageState: "missing", installScriptPath };
|
|
7036
|
+
}
|
|
7037
|
+
}
|
|
7038
|
+
function inspectClaudeNativeBinaryPlaceholder(path) {
|
|
7039
|
+
let fd;
|
|
7040
|
+
try {
|
|
7041
|
+
fd = openSync2(path, "r");
|
|
7042
|
+
const size = fstatSync(fd).size;
|
|
7043
|
+
if (size <= 0 || size > MAX_PLACEHOLDER_BYTES) return null;
|
|
7044
|
+
const bytes = Buffer.allocUnsafe(size);
|
|
7045
|
+
let offset = 0;
|
|
7046
|
+
while (offset < size) {
|
|
7047
|
+
const bytesRead = readSync(fd, bytes, offset, size - offset, offset);
|
|
7048
|
+
if (bytesRead === 0) break;
|
|
7049
|
+
offset += bytesRead;
|
|
7050
|
+
}
|
|
7051
|
+
if (offset !== size) return null;
|
|
7052
|
+
const text5 = bytes.toString("utf8");
|
|
7053
|
+
const signals = [
|
|
7054
|
+
text5.includes(MISSING_BINARY_MARKER),
|
|
7055
|
+
text5.includes(INSTALL_SCRIPT_MARKER),
|
|
7056
|
+
text5.includes(OMIT_OPTIONAL_MARKER) && text5.includes(IGNORE_SCRIPTS_MARKER)
|
|
7057
|
+
];
|
|
7058
|
+
if (signals.filter(Boolean).length < 2) return null;
|
|
7059
|
+
return inspectNativePackage(path);
|
|
7060
|
+
} catch {
|
|
7061
|
+
return null;
|
|
7062
|
+
} finally {
|
|
7063
|
+
if (fd !== void 0) {
|
|
7064
|
+
try {
|
|
7065
|
+
closeSync2(fd);
|
|
7066
|
+
} catch {
|
|
7067
|
+
}
|
|
7068
|
+
}
|
|
7069
|
+
}
|
|
7070
|
+
}
|
|
7071
|
+
|
|
6977
7072
|
// src/bun-entry-module.ts
|
|
6978
|
-
import { closeSync as
|
|
7073
|
+
import { closeSync as closeSync3, openSync as openSync3, readSync as readSync2, statSync as statSync6, writeSync as writeSync2 } from "fs";
|
|
6979
7074
|
import { execFileSync } from "child_process";
|
|
6980
7075
|
var BUN_TRAILER = Buffer.from("\n---- Bun! ----\n");
|
|
6981
7076
|
var BUN_OFFSETS_BYTES = 32;
|
|
@@ -7012,7 +7107,7 @@ function entryModuleShimName(byteLength) {
|
|
|
7012
7107
|
function readAt(fd, length, position) {
|
|
7013
7108
|
if (length <= 0 || position < 0) return null;
|
|
7014
7109
|
const buffer = Buffer.alloc(length);
|
|
7015
|
-
const read =
|
|
7110
|
+
const read = readSync2(fd, buffer, 0, length, position);
|
|
7016
7111
|
return read === length ? buffer : null;
|
|
7017
7112
|
}
|
|
7018
7113
|
function readBunModuleNames(fd, fileSize) {
|
|
@@ -7152,17 +7247,17 @@ function isMachO(fd) {
|
|
|
7152
7247
|
return value === 4277009102 || value === 4277009103 || value === 3472551422 || value === 3489328638 || value === 3405691582 || value === 3405691583;
|
|
7153
7248
|
}
|
|
7154
7249
|
function readBunModuleTable(path) {
|
|
7155
|
-
const fd =
|
|
7250
|
+
const fd = openSync3(path, "r");
|
|
7156
7251
|
try {
|
|
7157
|
-
return readBunModuleNames(fd,
|
|
7252
|
+
return readBunModuleNames(fd, statSync6(path).size);
|
|
7158
7253
|
} finally {
|
|
7159
|
-
|
|
7254
|
+
closeSync3(fd);
|
|
7160
7255
|
}
|
|
7161
7256
|
}
|
|
7162
7257
|
function readBunJavaScriptModules(path) {
|
|
7163
|
-
const fd =
|
|
7258
|
+
const fd = openSync3(path, "r");
|
|
7164
7259
|
try {
|
|
7165
|
-
const parsed = readBunModuleNames(fd,
|
|
7260
|
+
const parsed = readBunModuleNames(fd, statSync6(path).size);
|
|
7166
7261
|
if (!parsed) return null;
|
|
7167
7262
|
const modules = [];
|
|
7168
7263
|
for (let index = 0; index < parsed.names.length; index++) {
|
|
@@ -7182,33 +7277,33 @@ function readBunJavaScriptModules(path) {
|
|
|
7182
7277
|
}
|
|
7183
7278
|
return modules;
|
|
7184
7279
|
} finally {
|
|
7185
|
-
|
|
7280
|
+
closeSync3(fd);
|
|
7186
7281
|
}
|
|
7187
7282
|
}
|
|
7188
7283
|
function shimEntryModuleName(path) {
|
|
7189
|
-
const fd =
|
|
7284
|
+
const fd = openSync3(path, "r+");
|
|
7190
7285
|
try {
|
|
7191
|
-
const parsed = readBunModuleNames(fd,
|
|
7286
|
+
const parsed = readBunModuleNames(fd, statSync6(path).size);
|
|
7192
7287
|
if (!parsed) return null;
|
|
7193
7288
|
if (parsed.names.some(tweakccRecognizesModuleName)) return null;
|
|
7194
7289
|
const original = parsed.names[parsed.entryPointId];
|
|
7195
7290
|
const offset = parsed.offsets[parsed.entryPointId];
|
|
7196
7291
|
const marker = entryModuleShimName(Buffer.byteLength(original));
|
|
7197
7292
|
if (marker === null) return null;
|
|
7198
|
-
if (findStandIn(fd,
|
|
7293
|
+
if (findStandIn(fd, statSync6(path).size, marker).some((offset2) => isModuleNameAt(fd, offset2, marker))) {
|
|
7199
7294
|
return null;
|
|
7200
7295
|
}
|
|
7201
7296
|
writeNameBytes(fd, marker, offset);
|
|
7202
7297
|
return { offset, original, marker };
|
|
7203
7298
|
} finally {
|
|
7204
|
-
|
|
7299
|
+
closeSync3(fd);
|
|
7205
7300
|
}
|
|
7206
7301
|
}
|
|
7207
7302
|
function restoreEntryModuleName(path, shim, { resign }) {
|
|
7208
|
-
const fd =
|
|
7303
|
+
const fd = openSync3(path, "r+");
|
|
7209
7304
|
let machO;
|
|
7210
7305
|
try {
|
|
7211
|
-
const parsed = readBunModuleNames(fd,
|
|
7306
|
+
const parsed = readBunModuleNames(fd, statSync6(path).size);
|
|
7212
7307
|
const offset = parsed?.offsets[parsed.entryPointId];
|
|
7213
7308
|
if (!parsed || offset === void 0 || parsed.names[parsed.entryPointId] !== shim.marker) {
|
|
7214
7309
|
throw new Error(
|
|
@@ -7216,20 +7311,20 @@ function restoreEntryModuleName(path, shim, { resign }) {
|
|
|
7216
7311
|
);
|
|
7217
7312
|
}
|
|
7218
7313
|
writeNameBytes(fd, shim.original, offset);
|
|
7219
|
-
restoreEveryStandIn(fd,
|
|
7314
|
+
restoreEveryStandIn(fd, statSync6(path).size, shim);
|
|
7220
7315
|
machO = resign;
|
|
7221
7316
|
} finally {
|
|
7222
|
-
|
|
7317
|
+
closeSync3(fd);
|
|
7223
7318
|
}
|
|
7224
7319
|
if (machO) resignMachOBinary(path);
|
|
7225
7320
|
}
|
|
7226
7321
|
function resignMachOBinary(path) {
|
|
7227
|
-
const fd =
|
|
7322
|
+
const fd = openSync3(path, "r");
|
|
7228
7323
|
let machO;
|
|
7229
7324
|
try {
|
|
7230
7325
|
machO = isMachO(fd);
|
|
7231
7326
|
} finally {
|
|
7232
|
-
|
|
7327
|
+
closeSync3(fd);
|
|
7233
7328
|
}
|
|
7234
7329
|
if (machO && process.platform === "darwin") {
|
|
7235
7330
|
execFileSync("codesign", ["-s", "-", "-f", path], { stdio: "ignore" });
|
|
@@ -7237,7 +7332,7 @@ function resignMachOBinary(path) {
|
|
|
7237
7332
|
}
|
|
7238
7333
|
|
|
7239
7334
|
// src/bun-compiled-pointer.ts
|
|
7240
|
-
import { closeSync as
|
|
7335
|
+
import { closeSync as closeSync4, fstatSync as fstatSync2, openSync as openSync4, readSync as readSync3, writeSync as writeSync3 } from "fs";
|
|
7241
7336
|
var TWEAKCC_SCAN_STRIDE = 16384n;
|
|
7242
7337
|
var SCAN_CHUNK = 1 << 20;
|
|
7243
7338
|
var ELF_MAGIC = 1179403647;
|
|
@@ -7249,7 +7344,7 @@ function readAt2(fd, length, position) {
|
|
|
7249
7344
|
const buf = Buffer.alloc(length);
|
|
7250
7345
|
let read = 0;
|
|
7251
7346
|
while (read < length) {
|
|
7252
|
-
const n =
|
|
7347
|
+
const n = readSync3(fd, buf, read, length - read, position + read);
|
|
7253
7348
|
if (n <= 0) break;
|
|
7254
7349
|
read += n;
|
|
7255
7350
|
}
|
|
@@ -7269,7 +7364,7 @@ function readElfLayout(fd) {
|
|
|
7269
7364
|
const shstrndx = ident.readUInt16LE(62);
|
|
7270
7365
|
if (shnum === 0 || phnum === 65535) return null;
|
|
7271
7366
|
if (shstrndx >= shnum || shentsize < 64 || phentsize < 56) return null;
|
|
7272
|
-
const fileSize =
|
|
7367
|
+
const fileSize = fstatSync2(fd).size;
|
|
7273
7368
|
const fits = (offset, length) => Number.isSafeInteger(offset) && offset >= 0 && length >= 0 && offset + length <= fileSize;
|
|
7274
7369
|
if (!fits(Number(shoff), shnum * shentsize)) return null;
|
|
7275
7370
|
const shTable = readAt2(fd, shnum * shentsize, Number(shoff));
|
|
@@ -7358,7 +7453,7 @@ function scanForNeedle(fd, from, to, needle, skipFrom, skipTo) {
|
|
|
7358
7453
|
return hits;
|
|
7359
7454
|
}
|
|
7360
7455
|
function shimBunCompiledPointer(path) {
|
|
7361
|
-
const fd =
|
|
7456
|
+
const fd = openSync4(path, "r+");
|
|
7362
7457
|
try {
|
|
7363
7458
|
const layout = readElfLayout(fd);
|
|
7364
7459
|
if (!layout) return null;
|
|
@@ -7408,11 +7503,11 @@ function shimBunCompiledPointer(path) {
|
|
|
7408
7503
|
writeSync3(fd, needle, 0, 8, standInOffset);
|
|
7409
7504
|
return { pointerVaddr, standInVaddr, displaced, bunVaddr: bun.addr };
|
|
7410
7505
|
} finally {
|
|
7411
|
-
|
|
7506
|
+
closeSync4(fd);
|
|
7412
7507
|
}
|
|
7413
7508
|
}
|
|
7414
7509
|
function restoreBunCompiledPointer(path, shim) {
|
|
7415
|
-
const fd =
|
|
7510
|
+
const fd = openSync4(path, "r+");
|
|
7416
7511
|
try {
|
|
7417
7512
|
const layout = readElfLayout(fd);
|
|
7418
7513
|
if (!layout) throw new Error("the repacked binary is no longer a 64-bit little-endian ELF");
|
|
@@ -7449,12 +7544,12 @@ function restoreBunCompiledPointer(path, shim) {
|
|
|
7449
7544
|
throw new Error("the bytes the stand-in displaced were not restored");
|
|
7450
7545
|
}
|
|
7451
7546
|
} finally {
|
|
7452
|
-
|
|
7547
|
+
closeSync4(fd);
|
|
7453
7548
|
}
|
|
7454
7549
|
}
|
|
7455
7550
|
|
|
7456
7551
|
// src/bun-bundle.ts
|
|
7457
|
-
import { closeSync as
|
|
7552
|
+
import { closeSync as closeSync5, openSync as openSync5, readSync as readSync4, writeSync as writeSync4 } from "fs";
|
|
7458
7553
|
function writableModuleIndex(path) {
|
|
7459
7554
|
const table = readBunModuleTable(path);
|
|
7460
7555
|
if (!table) return null;
|
|
@@ -7570,18 +7665,18 @@ function placeholderOf(byteLength) {
|
|
|
7570
7665
|
return PLACEHOLDER_TEXT.repeat(Math.ceil(byteLength / PLACEHOLDER_TEXT.length)).slice(0, byteLength);
|
|
7571
7666
|
}
|
|
7572
7667
|
function readBlobData(path, table, into) {
|
|
7573
|
-
const fd =
|
|
7668
|
+
const fd = openSync5(path, "r");
|
|
7574
7669
|
try {
|
|
7575
7670
|
let read = 0;
|
|
7576
7671
|
while (read < table.byteCount) {
|
|
7577
|
-
const got =
|
|
7672
|
+
const got = readSync4(fd, into, read, table.byteCount - read, table.blobAt + read);
|
|
7578
7673
|
if (got <= 0) {
|
|
7579
7674
|
throw new Error(`read ${read} of the ${table.byteCount} blob bytes of ${path}`);
|
|
7580
7675
|
}
|
|
7581
7676
|
read += got;
|
|
7582
7677
|
}
|
|
7583
7678
|
} finally {
|
|
7584
|
-
|
|
7679
|
+
closeSync5(fd);
|
|
7585
7680
|
}
|
|
7586
7681
|
}
|
|
7587
7682
|
function repointModule(data, table, append) {
|
|
@@ -7633,7 +7728,7 @@ function applyBundleWritePlan(path, plan) {
|
|
|
7633
7728
|
}
|
|
7634
7729
|
const offsets = Buffer.from(plan.offsets);
|
|
7635
7730
|
offsets.writeBigUInt64LE(BigInt(byteCount), 0);
|
|
7636
|
-
const fd =
|
|
7731
|
+
const fd = openSync5(path, "r+");
|
|
7637
7732
|
try {
|
|
7638
7733
|
writeAll(fd, plan.data, repacked.blobAt, path);
|
|
7639
7734
|
const padding = byteCount - plan.data.length;
|
|
@@ -7641,7 +7736,7 @@ function applyBundleWritePlan(path, plan) {
|
|
|
7641
7736
|
writeAll(fd, offsets, repacked.blobAt + byteCount, path);
|
|
7642
7737
|
writeAll(fd, BUN_TRAILER2, repacked.blobAt + byteCount + BUN_OFFSETS_BYTES2, path);
|
|
7643
7738
|
} finally {
|
|
7644
|
-
|
|
7739
|
+
closeSync5(fd);
|
|
7645
7740
|
}
|
|
7646
7741
|
const published = readBunModuleTable(path);
|
|
7647
7742
|
if (!published) throw new Error(`cannot re-read the Bun module table of ${path} after publishing its blob`);
|
|
@@ -7677,15 +7772,15 @@ function writeAll(fd, bytes, position, path) {
|
|
|
7677
7772
|
|
|
7678
7773
|
// src/patch-backup.ts
|
|
7679
7774
|
import { createHash as createHash5 } from "crypto";
|
|
7680
|
-
import { existsSync as existsSync4, readFileSync as
|
|
7775
|
+
import { existsSync as existsSync4, readFileSync as readFileSync9, readdirSync, statSync as statSync7 } from "fs";
|
|
7681
7776
|
import { homedir as homedir2 } from "os";
|
|
7682
|
-
import { join as
|
|
7777
|
+
import { join as join7 } from "path";
|
|
7683
7778
|
var BACKUP_SHA_PREFIX_LENGTH = 16;
|
|
7684
7779
|
function backupDir() {
|
|
7685
|
-
return process.env["TWEAKCC_CONFIG_DIR"]?.trim() ||
|
|
7780
|
+
return process.env["TWEAKCC_CONFIG_DIR"]?.trim() || join7(homedir2(), ".tweakcc");
|
|
7686
7781
|
}
|
|
7687
7782
|
function sha256File(path) {
|
|
7688
|
-
return createHash5("sha256").update(
|
|
7783
|
+
return createHash5("sha256").update(readFileSync9(path)).digest("hex");
|
|
7689
7784
|
}
|
|
7690
7785
|
function backupVersionTag(version) {
|
|
7691
7786
|
const tag = version.trim().replace(/[^\w.-]+/g, "_");
|
|
@@ -7693,10 +7788,10 @@ function backupVersionTag(version) {
|
|
|
7693
7788
|
return tag;
|
|
7694
7789
|
}
|
|
7695
7790
|
function contentAddressedBackupPath(version, sha256, dir = backupDir()) {
|
|
7696
|
-
return
|
|
7791
|
+
return join7(dir, `claude-${backupVersionTag(version)}-${sha256.slice(0, BACKUP_SHA_PREFIX_LENGTH)}.orig`);
|
|
7697
7792
|
}
|
|
7698
7793
|
function tweakccMirrorBackupPath(dir = backupDir()) {
|
|
7699
|
-
return
|
|
7794
|
+
return join7(dir, "native-binary.backup");
|
|
7700
7795
|
}
|
|
7701
7796
|
function scanPristineBackups(version, dir = backupDir()) {
|
|
7702
7797
|
const tag = backupVersionTag(version);
|
|
@@ -7712,10 +7807,10 @@ function scanPristineBackups(version, dir = backupDir()) {
|
|
|
7712
7807
|
for (const entry of entries.sort()) {
|
|
7713
7808
|
const match = pattern.exec(entry);
|
|
7714
7809
|
if (!match) continue;
|
|
7715
|
-
const path =
|
|
7810
|
+
const path = join7(dir, entry);
|
|
7716
7811
|
let sha256;
|
|
7717
7812
|
try {
|
|
7718
|
-
if (!
|
|
7813
|
+
if (!statSync7(path).isFile()) continue;
|
|
7719
7814
|
sha256 = sha256File(path);
|
|
7720
7815
|
} catch {
|
|
7721
7816
|
corrupt.push(path);
|
|
@@ -7753,25 +7848,48 @@ function noBackupMessage(facts) {
|
|
|
7753
7848
|
const corrupt = facts.corruptBackups?.length ? ` (${facts.corruptBackups.length} backup file(s) for this version failed integrity checks and were ignored)` : "";
|
|
7754
7849
|
return `claude ${facts.version} is already patched and no trustworthy pristine backup for that version exists in ${backupDir()}${corrupt}. Reinstall Claude Code to get a pristine binary, then run \`clodex patch\`.`;
|
|
7755
7850
|
}
|
|
7851
|
+
function identifiesABackup(manifest) {
|
|
7852
|
+
return manifest.pristineSha256 !== void 0 || manifest.backupPath !== void 0;
|
|
7853
|
+
}
|
|
7854
|
+
function recordedBackupGoneMessage(facts, manifest) {
|
|
7855
|
+
const named = manifest.backupPath ?? `the backup holding sha256 ${manifest.pristineSha256}`;
|
|
7856
|
+
const corrupt = facts.corruptBackups?.length ? ` (${facts.corruptBackups.length} backup file(s) for this version failed integrity checks and were ignored)` : "";
|
|
7857
|
+
const others = facts.backups.length ? `The ${facts.backups.length} other pristine backup(s) tagged claude ${facts.version} there were made for some other install \u2014 two installs of one Claude Code version are different files, so restoring one of them would overwrite this install with bytes that were never its own. ` : "";
|
|
7858
|
+
return `The patch manifest records ${named} as the pristine content of ${facts.binaryPath}, and clodex cannot use it: it is missing from ${backupDir()}, failed its integrity check, or holds a different claude version${corrupt}. ${others}Reinstall Claude Code to get a pristine binary, then run \`clodex patch\`.`;
|
|
7859
|
+
}
|
|
7860
|
+
function otherInstallMessage(facts, otherBinaryPath) {
|
|
7861
|
+
return `The patch manifest records a different Claude Code install (${otherBinaryPath}), so nothing establishes that a pristine backup tagged claude ${facts.version} in ${backupDir()} belongs to ${facts.binaryPath}. Two installs of one Claude Code version are different files, so restoring by version tag alone would overwrite this install with another one's bytes. Set TWEAKCC_CC_INSTALLATION_PATH=${otherBinaryPath} to restore that install instead, or reinstall Claude Code to make this one pristine.`;
|
|
7862
|
+
}
|
|
7756
7863
|
function selectRestoreSource(facts) {
|
|
7757
7864
|
const notes = [];
|
|
7758
|
-
const
|
|
7865
|
+
const recorded = facts.manifest;
|
|
7866
|
+
const speaksForThisVersion = !recorded?.claudeVersion || recorded.claudeVersion === facts.version;
|
|
7867
|
+
const manifest = recorded && recorded.binaryPath === facts.binaryPath && speaksForThisVersion ? recorded : null;
|
|
7868
|
+
const other = recorded && recorded.binaryPath !== facts.binaryPath && speaksForThisVersion ? recorded : null;
|
|
7759
7869
|
let chosen = manifest?.pristineSha256 ? facts.backups.find((backup) => backup.sha256 === manifest.pristineSha256) : void 0;
|
|
7760
7870
|
if (!chosen && manifest?.backupPath) {
|
|
7761
7871
|
chosen = facts.backups.find((backup) => backup.path === manifest.backupPath);
|
|
7762
|
-
|
|
7763
|
-
|
|
7764
|
-
}
|
|
7872
|
+
}
|
|
7873
|
+
if (!chosen && manifest && identifiesABackup(manifest)) {
|
|
7874
|
+
return { action: "error", message: recordedBackupGoneMessage(facts, manifest) };
|
|
7765
7875
|
}
|
|
7766
7876
|
if (!chosen) {
|
|
7877
|
+
if (other) {
|
|
7878
|
+
return facts.backups.length ? { action: "error", message: otherInstallMessage(facts, other.binaryPath) } : { action: "error", message: noBackupMessage(facts) };
|
|
7879
|
+
}
|
|
7767
7880
|
const distinct = [...new Set(facts.backups.map((backup) => backup.sha256))];
|
|
7768
7881
|
if (distinct.length > 1) {
|
|
7769
7882
|
return {
|
|
7770
7883
|
action: "error",
|
|
7771
|
-
message: `Found conflicting pristine backups for claude ${facts.version}: ${facts.backups.map((backup) => backup.path).join(", ")}. They do not hold the same bytes, so clodex cannot tell which one is pristine.
|
|
7884
|
+
message: `Found conflicting pristine backups for claude ${facts.version}: ${facts.backups.map((backup) => backup.path).join(", ")}. They do not hold the same bytes, so clodex cannot tell which one is pristine. If this machine has more than one Claude Code install, both are probably genuine \u2014 one per install \u2014 and deleting either one is a guess. Reinstall the Claude Code at ${facts.binaryPath} instead: a pristine install needs no backup, and \`clodex patch\` will record its own.`
|
|
7772
7885
|
};
|
|
7773
7886
|
}
|
|
7774
7887
|
chosen = facts.backups.find((backup) => backup.kind === "content-addressed") ?? facts.backups[0];
|
|
7888
|
+
if (chosen) {
|
|
7889
|
+
notes.push(
|
|
7890
|
+
`No patch manifest records ${facts.binaryPath}, so ${chosen.path} is being used as its pristine content on the strength of its claude ${facts.version} version tag alone. On a machine with more than one Claude Code install those bytes may belong to the other one \u2014 a clodex that had recorded this install would have refused rather than guess.`
|
|
7891
|
+
);
|
|
7892
|
+
}
|
|
7775
7893
|
}
|
|
7776
7894
|
if (!chosen) return { action: "error", message: noBackupMessage(facts) };
|
|
7777
7895
|
return {
|
|
@@ -8599,8 +8717,8 @@ var FAILURE_EVENT_TYPES = /* @__PURE__ */ new Set(["error", "response.failed", "
|
|
|
8599
8717
|
var RESPONSES_WS_HARD_TTL_MS = 55 * 6e4;
|
|
8600
8718
|
var RESPONSES_WS_IDLE_TTL_MS = 30 * 6e4;
|
|
8601
8719
|
var RESPONSES_WS_NURSERY_IDLE_TTL_MS = 5 * 6e4;
|
|
8602
|
-
var RESPONSES_WS_MAX_CONNECTIONS =
|
|
8603
|
-
var RESPONSES_WS_MAX_NURSERY_CONNECTIONS =
|
|
8720
|
+
var RESPONSES_WS_MAX_CONNECTIONS = 64;
|
|
8721
|
+
var RESPONSES_WS_MAX_NURSERY_CONNECTIONS = 48;
|
|
8604
8722
|
var diagnosticContext = new AsyncLocalStorage();
|
|
8605
8723
|
function withResponsesWebSocketDiagnosticContext(context, fn) {
|
|
8606
8724
|
return diagnosticContext.run(context, fn);
|
|
@@ -8982,13 +9100,16 @@ function mismatchDumpLine(items, index) {
|
|
|
8982
9100
|
function canonicalItemStrings(items) {
|
|
8983
9101
|
return items.map((item) => canonicalJson(normalizeToolCallJson([item])));
|
|
8984
9102
|
}
|
|
8985
|
-
function
|
|
8986
|
-
if (client.length
|
|
9103
|
+
function isPrefixOrEqual(head, client) {
|
|
9104
|
+
if (client.length < head.length) return false;
|
|
8987
9105
|
for (let index = 0; index < head.length; index += 1) {
|
|
8988
9106
|
if (head[index] !== client[index]) return false;
|
|
8989
9107
|
}
|
|
8990
9108
|
return true;
|
|
8991
9109
|
}
|
|
9110
|
+
function isStrictPrefix(head, client) {
|
|
9111
|
+
return client.length > head.length && isPrefixOrEqual(head, client);
|
|
9112
|
+
}
|
|
8992
9113
|
function continuationMatch(entry, payload, clientItems) {
|
|
8993
9114
|
if (!entry.responseId || !entry.requestInput || !entry.expectedAssistant) return void 0;
|
|
8994
9115
|
const full = inputArray(payload);
|
|
@@ -9481,11 +9602,16 @@ function evictOldestIdleGeneration(generation, maxConnections, reason) {
|
|
|
9481
9602
|
while (connectionCountByGeneration(generation) >= maxConnections && idle.length) {
|
|
9482
9603
|
const oldest = idle.shift();
|
|
9483
9604
|
if (oldest) {
|
|
9605
|
+
const idleMs = Math.max(0, oldest.options.now() - oldest.lastUsedAt);
|
|
9606
|
+
oldest.debug(
|
|
9607
|
+
`evicting the oldest idle ${generation} connection to stay within its cap: connection=${oldest.debugId} idle_ms=${idleMs} cap=${maxConnections} reason=${reason}`
|
|
9608
|
+
);
|
|
9484
9609
|
evictions.push({
|
|
9485
9610
|
connectionId: oldest.debugId,
|
|
9486
9611
|
partitionKey: oldest.key,
|
|
9487
9612
|
generation: oldest.generation,
|
|
9488
|
-
reason
|
|
9613
|
+
reason,
|
|
9614
|
+
idleMs
|
|
9489
9615
|
});
|
|
9490
9616
|
deleteEntry(oldest);
|
|
9491
9617
|
}
|
|
@@ -9905,16 +10031,28 @@ function createResponsesWebSocketFetch(wsUrl, log12, options = {}) {
|
|
|
9905
10031
|
const diagnosticCorrelation = diagnosticContext.getStore();
|
|
9906
10032
|
let now = resolvedOptions.now();
|
|
9907
10033
|
const evictions = cleanupExpiredConnections(now);
|
|
10034
|
+
let canonicalClientItems;
|
|
10035
|
+
const clientItems = () => canonicalClientItems ??= canonicalItemStrings(inputArray(payload));
|
|
9908
10036
|
const scanForHeads = () => {
|
|
9909
10037
|
const scanned = partitionKey ? connectionEntries(partitionKey) : [];
|
|
9910
10038
|
const idle = scanned.filter((entry) => !entry.inFlight);
|
|
9911
|
-
const
|
|
10039
|
+
const canonical = idle.length ? clientItems() : [];
|
|
9912
10040
|
return {
|
|
9913
10041
|
candidates: scanned,
|
|
9914
10042
|
idleCandidates: idle,
|
|
9915
|
-
matches: idle.map((entry) => ({ entry, match: continuationMatch(entry, payload,
|
|
10043
|
+
matches: idle.map((entry) => ({ entry, match: continuationMatch(entry, payload, canonical) })).filter((candidate) => candidate.match !== void 0).sort((left, right) => left.match.delta.length - right.match.delta.length || (left.match.mode === right.match.mode ? 0 : left.match.mode === "exact" ? -1 : 1))
|
|
9916
10044
|
};
|
|
9917
10045
|
};
|
|
10046
|
+
const couldPrecedeThisRequest = (entry) => {
|
|
10047
|
+
if (entry.responseId && entry.requestInput && entry.expectedAssistant) {
|
|
10048
|
+
return continuationMatch(entry, payload, clientItems()) !== void 0;
|
|
10049
|
+
}
|
|
10050
|
+
const streaming = entry.current;
|
|
10051
|
+
if (!streaming) return true;
|
|
10052
|
+
streaming.canonicalInput ??= canonicalItemStrings(inputArray(streaming.originalPayload));
|
|
10053
|
+
return isPrefixOrEqual(streaming.canonicalInput, clientItems());
|
|
10054
|
+
};
|
|
10055
|
+
const blockingHead = (entries) => entries.find((entry) => entry.inFlight && couldPrecedeThisRequest(entry));
|
|
9918
10056
|
let { candidates, idleCandidates, matches } = scanForHeads();
|
|
9919
10057
|
let selected = matches[0]?.entry;
|
|
9920
10058
|
let selectedMatch = matches[0]?.match;
|
|
@@ -9957,9 +10095,10 @@ function createResponsesWebSocketFetch(wsUrl, log12, options = {}) {
|
|
|
9957
10095
|
);
|
|
9958
10096
|
return "continuation";
|
|
9959
10097
|
};
|
|
10098
|
+
let arrivalBlockingHead = selected ? void 0 : blockingHead(candidates);
|
|
9960
10099
|
if (selected && selectedDelta) {
|
|
9961
10100
|
decision = continueOnHead(selected, selectedMatch);
|
|
9962
|
-
} else if (
|
|
10101
|
+
} else if (arrivalBlockingHead) {
|
|
9963
10102
|
selected = void 0;
|
|
9964
10103
|
persistent = false;
|
|
9965
10104
|
decision = "parallel_isolated";
|
|
@@ -10058,11 +10197,15 @@ function createResponsesWebSocketFetch(wsUrl, log12, options = {}) {
|
|
|
10058
10197
|
pacingRescanOutcome = "no_change";
|
|
10059
10198
|
}
|
|
10060
10199
|
}
|
|
10061
|
-
if (!selected && persistent && partitionKey
|
|
10062
|
-
|
|
10063
|
-
|
|
10064
|
-
|
|
10065
|
-
|
|
10200
|
+
if (!selected && persistent && partitionKey) {
|
|
10201
|
+
const blocked = blockingHead(connectionEntries(partitionKey));
|
|
10202
|
+
if (blocked) {
|
|
10203
|
+
arrivalBlockingHead = blocked;
|
|
10204
|
+
persistent = false;
|
|
10205
|
+
decision = "parallel_isolated";
|
|
10206
|
+
debug("parallel request using an isolated socket after pacing");
|
|
10207
|
+
if (pacingRescanOutcome === "no_change") pacingRescanOutcome = "parallel_isolated";
|
|
10208
|
+
}
|
|
10066
10209
|
}
|
|
10067
10210
|
}
|
|
10068
10211
|
let suppressedMismatchWarnings;
|
|
@@ -10118,6 +10261,10 @@ function createResponsesWebSocketFetch(wsUrl, log12, options = {}) {
|
|
|
10118
10261
|
createdConnectionId: selected ? void 0 : nextConnectionDebugId,
|
|
10119
10262
|
...pacingWaitedMs !== void 0 ? { pacingWaitedMs } : {},
|
|
10120
10263
|
...pacingRescanOutcome !== void 0 ? { pacingRescanOutcome } : {},
|
|
10264
|
+
// Which busy head this request could not be told apart from. Without it an
|
|
10265
|
+
// isolated turn reads as unexplained, and isolation is the single largest
|
|
10266
|
+
// source of uncached prompt tokens on this transport.
|
|
10267
|
+
...decision === "parallel_isolated" && arrivalBlockingHead ? { isolatedByConnectionId: arrivalBlockingHead.debugId } : {},
|
|
10121
10268
|
...suppressedMismatchWarnings !== void 0 ? { suppressedMismatchWarnings } : {},
|
|
10122
10269
|
createdGeneration: selected ? void 0 : persistent ? "nursery" : "isolated",
|
|
10123
10270
|
incrementalInputItems: selectedDelta?.length,
|
|
@@ -11143,7 +11290,7 @@ function thinkingProviderOptions(npm) {
|
|
|
11143
11290
|
|
|
11144
11291
|
// src/proxy.ts
|
|
11145
11292
|
import { createServer } from "http";
|
|
11146
|
-
import { appendFileSync as appendFileSync2, openSync as
|
|
11293
|
+
import { appendFileSync as appendFileSync2, openSync as openSync6, writeSync as writeSync5, closeSync as closeSync6 } from "fs";
|
|
11147
11294
|
|
|
11148
11295
|
// src/http-utils.ts
|
|
11149
11296
|
import * as zlib from "zlib";
|
|
@@ -11709,45 +11856,125 @@ function resolveUpstreamTools(tools, messages) {
|
|
|
11709
11856
|
|
|
11710
11857
|
// src/tool-schema-sanitize.ts
|
|
11711
11858
|
function isCompatiblePattern(pattern) {
|
|
11859
|
+
let inClass = false;
|
|
11712
11860
|
for (let i = 0; i < pattern.length; i++) {
|
|
11713
|
-
|
|
11861
|
+
const ch = pattern[i];
|
|
11862
|
+
if (ch === "\\") {
|
|
11714
11863
|
const next = pattern[i + 1];
|
|
11715
|
-
if ((next === "p" || next === "P") && pattern[i + 2] === "{") return false;
|
|
11864
|
+
if ((next === "p" || next === "P" || next === "u" || next === "x") && pattern[i + 2] === "{") return false;
|
|
11716
11865
|
if (next === "k" && pattern[i + 2] === "<") return false;
|
|
11717
11866
|
i++;
|
|
11718
11867
|
continue;
|
|
11719
11868
|
}
|
|
11720
|
-
if (
|
|
11869
|
+
if (inClass) {
|
|
11870
|
+
if (ch === "]") inClass = false;
|
|
11871
|
+
continue;
|
|
11872
|
+
}
|
|
11873
|
+
if (ch === "[") {
|
|
11874
|
+
inClass = true;
|
|
11875
|
+
continue;
|
|
11876
|
+
}
|
|
11877
|
+
if (ch === "(" && pattern[i + 1] === "?" && pattern[i + 2] === "<" && pattern[i + 3] !== "=" && pattern[i + 3] !== "!") return false;
|
|
11721
11878
|
}
|
|
11722
11879
|
return true;
|
|
11723
11880
|
}
|
|
11881
|
+
var SUBSCHEMA_KEYWORDS = /* @__PURE__ */ new Set([
|
|
11882
|
+
"items",
|
|
11883
|
+
"prefixItems",
|
|
11884
|
+
"additionalItems",
|
|
11885
|
+
"unevaluatedItems",
|
|
11886
|
+
"additionalProperties",
|
|
11887
|
+
"unevaluatedProperties",
|
|
11888
|
+
"propertyNames",
|
|
11889
|
+
"contains",
|
|
11890
|
+
"anyOf",
|
|
11891
|
+
"oneOf",
|
|
11892
|
+
"allOf",
|
|
11893
|
+
"not",
|
|
11894
|
+
"if",
|
|
11895
|
+
"then",
|
|
11896
|
+
"else",
|
|
11897
|
+
"contentSchema"
|
|
11898
|
+
]);
|
|
11899
|
+
var SUBSCHEMA_MAP_KEYWORDS = /* @__PURE__ */ new Set([
|
|
11900
|
+
"properties",
|
|
11901
|
+
"$defs",
|
|
11902
|
+
"definitions",
|
|
11903
|
+
"dependentSchemas",
|
|
11904
|
+
"dependencies"
|
|
11905
|
+
]);
|
|
11724
11906
|
function sanitizeToolSchema(schema) {
|
|
11725
|
-
|
|
11907
|
+
return sanitizeSchema(schema);
|
|
11908
|
+
}
|
|
11909
|
+
function sanitizeSchema(node) {
|
|
11910
|
+
if (Array.isArray(node)) {
|
|
11726
11911
|
let changed2 = false;
|
|
11727
|
-
const
|
|
11728
|
-
const next =
|
|
11912
|
+
const out = node.map((entry) => {
|
|
11913
|
+
const next = sanitizeSchema(entry);
|
|
11729
11914
|
changed2 ||= next !== entry;
|
|
11730
11915
|
return next;
|
|
11731
11916
|
});
|
|
11732
|
-
return changed2 ?
|
|
11917
|
+
return changed2 ? out : node;
|
|
11733
11918
|
}
|
|
11734
|
-
if (!
|
|
11919
|
+
if (!node || typeof node !== "object") return node;
|
|
11920
|
+
const record = node;
|
|
11921
|
+
const patternProps = "patternProperties" in record ? sanitizePatternProperties(record.patternProperties) : void 0;
|
|
11922
|
+
const openClosure = patternProps?.dropped === true;
|
|
11735
11923
|
let changed = false;
|
|
11736
|
-
const
|
|
11737
|
-
for (const [key, value] of Object.entries(
|
|
11738
|
-
if (key === "
|
|
11739
|
-
|
|
11924
|
+
const entries = [];
|
|
11925
|
+
for (const [key, value] of Object.entries(record)) {
|
|
11926
|
+
if (key === "patternProperties" && patternProps) {
|
|
11927
|
+
changed ||= patternProps.value !== value;
|
|
11928
|
+
entries.push([key, patternProps.value]);
|
|
11929
|
+
continue;
|
|
11930
|
+
}
|
|
11931
|
+
if (openClosure && value !== true && (key === "additionalProperties" || key === "unevaluatedProperties")) {
|
|
11932
|
+
changed = true;
|
|
11933
|
+
continue;
|
|
11934
|
+
}
|
|
11935
|
+
if (key === "pattern") {
|
|
11936
|
+
if (typeof value === "string" && !isCompatiblePattern(value)) {
|
|
11740
11937
|
changed = true;
|
|
11741
11938
|
continue;
|
|
11742
11939
|
}
|
|
11743
|
-
|
|
11940
|
+
entries.push([key, value]);
|
|
11744
11941
|
continue;
|
|
11745
11942
|
}
|
|
11746
|
-
|
|
11943
|
+
let next = value;
|
|
11944
|
+
if (SUBSCHEMA_KEYWORDS.has(key)) next = sanitizeSchema(value);
|
|
11945
|
+
else if (SUBSCHEMA_MAP_KEYWORDS.has(key)) next = sanitizeSchemaMap(value);
|
|
11747
11946
|
changed ||= next !== value;
|
|
11748
|
-
|
|
11947
|
+
entries.push([key, next]);
|
|
11749
11948
|
}
|
|
11750
|
-
return changed ?
|
|
11949
|
+
return changed ? Object.fromEntries(entries) : node;
|
|
11950
|
+
}
|
|
11951
|
+
function sanitizeSchemaMap(node) {
|
|
11952
|
+
if (!node || typeof node !== "object" || Array.isArray(node)) return node;
|
|
11953
|
+
let changed = false;
|
|
11954
|
+
const entries = [];
|
|
11955
|
+
for (const [key, value] of Object.entries(node)) {
|
|
11956
|
+
const next = sanitizeSchema(value);
|
|
11957
|
+
changed ||= next !== value;
|
|
11958
|
+
entries.push([key, next]);
|
|
11959
|
+
}
|
|
11960
|
+
return changed ? Object.fromEntries(entries) : node;
|
|
11961
|
+
}
|
|
11962
|
+
function sanitizePatternProperties(node) {
|
|
11963
|
+
if (!node || typeof node !== "object" || Array.isArray(node)) return { value: node, dropped: false };
|
|
11964
|
+
let changed = false;
|
|
11965
|
+
let dropped = false;
|
|
11966
|
+
const entries = [];
|
|
11967
|
+
for (const [key, value] of Object.entries(node)) {
|
|
11968
|
+
if (!isCompatiblePattern(key)) {
|
|
11969
|
+
changed = true;
|
|
11970
|
+
dropped = true;
|
|
11971
|
+
continue;
|
|
11972
|
+
}
|
|
11973
|
+
const next = sanitizeSchema(value);
|
|
11974
|
+
changed ||= next !== value;
|
|
11975
|
+
entries.push([key, next]);
|
|
11976
|
+
}
|
|
11977
|
+
return { value: changed ? Object.fromEntries(entries) : node, dropped };
|
|
11751
11978
|
}
|
|
11752
11979
|
|
|
11753
11980
|
// src/upstream-attempts.ts
|
|
@@ -12849,12 +13076,12 @@ function createTranslationLifecycle(logPath, requestId, claudeSessionId, modelId
|
|
|
12849
13076
|
function appendSecureLog(logPath, line) {
|
|
12850
13077
|
const redacted = redactTraceLine(line);
|
|
12851
13078
|
try {
|
|
12852
|
-
const fd =
|
|
13079
|
+
const fd = openSync6(logPath, "a", 384);
|
|
12853
13080
|
try {
|
|
12854
13081
|
writeSync5(fd, `${(/* @__PURE__ */ new Date()).toISOString()} ${redacted}
|
|
12855
13082
|
`);
|
|
12856
13083
|
} finally {
|
|
12857
|
-
|
|
13084
|
+
closeSync6(fd);
|
|
12858
13085
|
}
|
|
12859
13086
|
} catch {
|
|
12860
13087
|
try {
|
|
@@ -13538,14 +13765,14 @@ function buildHttpProxyRoutes(providers, favorites, modelAliases = void 0, max =
|
|
|
13538
13765
|
|
|
13539
13766
|
// src/patcher.ts
|
|
13540
13767
|
function getPatchManifestPath() {
|
|
13541
|
-
return
|
|
13768
|
+
return join8(getAppHome(), "patch-state.json");
|
|
13542
13769
|
}
|
|
13543
13770
|
function getPatchLockPath() {
|
|
13544
|
-
return
|
|
13771
|
+
return join8(getAppHome(), "patch.lock");
|
|
13545
13772
|
}
|
|
13546
13773
|
function readPatchManifest(path = getPatchManifestPath()) {
|
|
13547
13774
|
try {
|
|
13548
|
-
const parsed = JSON.parse(
|
|
13775
|
+
const parsed = JSON.parse(readFileSync10(path, "utf8"));
|
|
13549
13776
|
if (parsed && typeof parsed.binaryPath === "string" && typeof parsed.configHash === "string") {
|
|
13550
13777
|
return parsed;
|
|
13551
13778
|
}
|
|
@@ -13708,13 +13935,13 @@ function pidIsAlive(pid) {
|
|
|
13708
13935
|
function tryAcquirePatchLock(lockPath = getPatchLockPath(), opts = {}) {
|
|
13709
13936
|
const now = opts.now ?? Date.now();
|
|
13710
13937
|
const isAlive = opts.isAlive ?? pidIsAlive;
|
|
13711
|
-
mkdirSync5(
|
|
13938
|
+
mkdirSync5(join8(lockPath, ".."), { recursive: true, mode: 448 });
|
|
13712
13939
|
for (let attempt = 0; attempt < 2; attempt++) {
|
|
13713
13940
|
try {
|
|
13714
|
-
const fd =
|
|
13941
|
+
const fd = openSync7(lockPath, "wx");
|
|
13715
13942
|
const content = { pid: process.pid, startedAt: now };
|
|
13716
13943
|
writeFileSync5(fd, JSON.stringify(content));
|
|
13717
|
-
|
|
13944
|
+
closeSync7(fd);
|
|
13718
13945
|
return () => {
|
|
13719
13946
|
try {
|
|
13720
13947
|
unlinkSync3(lockPath);
|
|
@@ -13724,7 +13951,7 @@ function tryAcquirePatchLock(lockPath = getPatchLockPath(), opts = {}) {
|
|
|
13724
13951
|
} catch {
|
|
13725
13952
|
let stale = false;
|
|
13726
13953
|
try {
|
|
13727
|
-
const existing = JSON.parse(
|
|
13954
|
+
const existing = JSON.parse(readFileSync10(lockPath, "utf8"));
|
|
13728
13955
|
stale = !existing.pid || !isAlive(existing.pid) || typeof existing.startedAt === "number" && now - existing.startedAt > PATCH_LOCK_STALE_MS;
|
|
13729
13956
|
} catch {
|
|
13730
13957
|
stale = true;
|
|
@@ -13740,7 +13967,7 @@ function tryAcquirePatchLock(lockPath = getPatchLockPath(), opts = {}) {
|
|
|
13740
13967
|
}
|
|
13741
13968
|
function resolveClaudeBinaryForPatch() {
|
|
13742
13969
|
const envOverride = process.env["TWEAKCC_CC_INSTALLATION_PATH"];
|
|
13743
|
-
const nativeSymlink =
|
|
13970
|
+
const nativeSymlink = join8(homedir3(), ".local", "bin", "claude");
|
|
13744
13971
|
const source = envOverride?.trim() || (existsSync5(nativeSymlink) ? nativeSymlink : null) || findClaudeBinary();
|
|
13745
13972
|
if (!source) return { ok: false, reason: "binary-not-found" };
|
|
13746
13973
|
let resolved;
|
|
@@ -13762,18 +13989,37 @@ function resolveClaudeBinaryForPatch() {
|
|
|
13762
13989
|
}
|
|
13763
13990
|
resolved = followed.path;
|
|
13764
13991
|
try {
|
|
13765
|
-
if (!
|
|
13992
|
+
if (!statSync8(resolved).isFile()) return { ok: false, reason: "binary-not-found" };
|
|
13766
13993
|
} catch {
|
|
13767
13994
|
return { ok: false, reason: "binary-not-found" };
|
|
13768
13995
|
}
|
|
13996
|
+
const placeholder = inspectClaudeNativeBinaryPlaceholder(resolved);
|
|
13997
|
+
if (placeholder) {
|
|
13998
|
+
return {
|
|
13999
|
+
ok: false,
|
|
14000
|
+
reason: "native-binary-missing",
|
|
14001
|
+
binaryPath: resolved,
|
|
14002
|
+
...placeholder
|
|
14003
|
+
};
|
|
14004
|
+
}
|
|
13769
14005
|
const version = getClaudeVersionForBinary(resolved);
|
|
13770
14006
|
if (!version) return { ok: false, reason: "version-unknown", binaryPath: resolved };
|
|
13771
14007
|
return { ok: true, binaryPath: resolved, version };
|
|
13772
14008
|
}
|
|
13773
|
-
function
|
|
14009
|
+
function installScriptCommand(path) {
|
|
14010
|
+
return path === null ? "`node node_modules/@anthropic-ai/claude-code/install.cjs` (adjust the path for a local or global install)" : `\`node "${path}"\``;
|
|
14011
|
+
}
|
|
14012
|
+
function describePatchTargetFailure(target, command = "patch") {
|
|
13774
14013
|
if (target.reason === "launcher-unresolved") {
|
|
13775
14014
|
return `${target.shimPath} starts Claude Code but is not Claude Code itself, and clodex could not follow it: ${target.detail}. clodex will not patch a launcher script \u2014 that fails with "Unable to detect installation type". Set CLODEX_CLAUDE_PATH to the Claude Code program itself (for an npm install on Windows that is node_modules\\@anthropic-ai\\claude-code\\bin\\claude.exe under the directory holding the launcher), then run the command again.`;
|
|
13776
14015
|
}
|
|
14016
|
+
if (target.reason === "native-binary-missing") {
|
|
14017
|
+
const installer = installScriptCommand(target.installScriptPath);
|
|
14018
|
+
const remedy = target.nativePackageState === "present" ? `The platform-native package is installed, so run ${installer} to finish the installation.` : target.nativePackageState === "missing" ? "The platform-native optional package is missing, so `install.cjs` cannot repair this state. Reinstall Claude Code without `--ignore-scripts` / `--omit=optional`." : `clodex could not determine whether the platform-native package is installed. If it is, run ${installer}; otherwise reinstall Claude Code without \`--ignore-scripts\` / \`--omit=optional\`.`;
|
|
14019
|
+
const retry = command === "restore" ? "Then run `clodex patch --restore` again." : command === "launch" ? "Then run the command again." : "Then run `clodex patch` again.";
|
|
14020
|
+
const restore = command === "restore" ? ` If you need to restore by hand, pristine backups are in ${backupDir()}.` : "";
|
|
14021
|
+
return `${target.binaryPath} is Claude Code's npm placeholder, not its native binary, so the npm install is incomplete. ${remedy} ${retry}${restore} If this is a custom wrapper rather than the npm placeholder, set TWEAKCC_CC_INSTALLATION_PATH to the native Claude Code binary.`;
|
|
14022
|
+
}
|
|
13777
14023
|
return target.reason === "binary-not-found" ? "claude binary not found. Install Claude Code or set TWEAKCC_CC_INSTALLATION_PATH." : `Could not determine the version of ${target.binaryPath} (\`claude --version\` failed). clodex will not patch a binary whose version it cannot read, because the version selects the pristine backup it patches from. If a previous patch left the install broken, \`clodex patch --restore\` still works \u2014 it reads the version from the patch manifest.`;
|
|
13778
14024
|
}
|
|
13779
14025
|
function summarizePatchResults(results) {
|
|
@@ -13870,8 +14116,8 @@ async function applyPatch(binaryPath, version, desired, configHash, opts) {
|
|
|
13870
14116
|
try {
|
|
13871
14117
|
mkdirSync5(backupDir(), { recursive: true });
|
|
13872
14118
|
const { tryDetectInstallation, readContent, writeContent } = await import("tweakcc");
|
|
13873
|
-
candidateDir = mkdtempSync(
|
|
13874
|
-
const candidatePath =
|
|
14119
|
+
candidateDir = mkdtempSync(join8(dirname5(binaryPath), ".clodex-patch-"));
|
|
14120
|
+
const candidatePath = join8(candidateDir, basename(binaryPath));
|
|
13875
14121
|
const seedCandidate = async (from) => {
|
|
13876
14122
|
copyFileSync(from, candidatePath);
|
|
13877
14123
|
const shim = shimEntryModuleName(candidatePath);
|
|
@@ -14001,7 +14247,7 @@ async function applyPatch(binaryPath, version, desired, configHash, opts) {
|
|
|
14001
14247
|
}
|
|
14002
14248
|
if (writeShim) restoreEntryModuleName(candidatePath, writeShim, { resign: true });
|
|
14003
14249
|
else if (publishedBlob) resignMachOBinary(candidatePath);
|
|
14004
|
-
patchedSize =
|
|
14250
|
+
patchedSize = statSync8(candidatePath).size;
|
|
14005
14251
|
patchedSha256 = sha256File(candidatePath);
|
|
14006
14252
|
renameSync2(candidatePath, binaryPath);
|
|
14007
14253
|
} catch (err) {
|
|
@@ -14054,12 +14300,12 @@ async function applyPatch(binaryPath, version, desired, configHash, opts) {
|
|
|
14054
14300
|
};
|
|
14055
14301
|
}
|
|
14056
14302
|
function runRestoreCommand(target) {
|
|
14057
|
-
if (!target.ok && target.reason === "binary-not-found") {
|
|
14058
|
-
p2.log.error(describePatchTargetFailure(target));
|
|
14303
|
+
if (!target.ok && (target.reason === "binary-not-found" || target.reason === "native-binary-missing")) {
|
|
14304
|
+
p2.log.error(describePatchTargetFailure(target, "restore"));
|
|
14059
14305
|
return 1;
|
|
14060
14306
|
}
|
|
14061
14307
|
if (!target.ok && target.reason === "launcher-unresolved" && target.declaredTarget === null) {
|
|
14062
|
-
p2.log.error(describePatchTargetFailure(target));
|
|
14308
|
+
p2.log.error(describePatchTargetFailure(target, "restore"));
|
|
14063
14309
|
return 1;
|
|
14064
14310
|
}
|
|
14065
14311
|
const binaryPath = target.binaryPath;
|
|
@@ -14130,7 +14376,7 @@ async function runPatchCommand(opts = {}) {
|
|
|
14130
14376
|
binaryPath,
|
|
14131
14377
|
claudeVersion: version,
|
|
14132
14378
|
configHash,
|
|
14133
|
-
binarySize:
|
|
14379
|
+
binarySize: statSync8(binaryPath).size
|
|
14134
14380
|
});
|
|
14135
14381
|
if (state === "current") {
|
|
14136
14382
|
p2.log.success(`claude ${version} is already patched with the current model config \u2014 nothing to do.`);
|
|
@@ -14168,7 +14414,7 @@ async function runLaunchPatchCheck(opts = {}) {
|
|
|
14168
14414
|
const target = resolveClaudeBinaryForPatch();
|
|
14169
14415
|
if (!target.ok) {
|
|
14170
14416
|
if (target.reason !== "binary-not-found" && !opts.agentStdout) {
|
|
14171
|
-
console.error(pc3.dim(`clodex: ${describePatchTargetFailure(target)}`));
|
|
14417
|
+
console.error(pc3.dim(`clodex: ${describePatchTargetFailure(target, "launch")}`));
|
|
14172
14418
|
}
|
|
14173
14419
|
return;
|
|
14174
14420
|
}
|
|
@@ -14186,7 +14432,7 @@ async function runLaunchPatchCheck(opts = {}) {
|
|
|
14186
14432
|
binaryPath: resolved.binaryPath,
|
|
14187
14433
|
claudeVersion: resolved.version,
|
|
14188
14434
|
configHash,
|
|
14189
|
-
binarySize:
|
|
14435
|
+
binarySize: statSync8(resolved.binaryPath).size
|
|
14190
14436
|
});
|
|
14191
14437
|
if (state === "current") return;
|
|
14192
14438
|
const interactive = !opts.dryRun && !opts.agentStdout && process.stdin.isTTY === true && process.stdout.isTTY === true;
|
|
@@ -14522,19 +14768,19 @@ function localProvidersToServerModels(localProviders) {
|
|
|
14522
14768
|
// src/registry/credential-cleanup-journal.ts
|
|
14523
14769
|
import { randomUUID as randomUUID4 } from "crypto";
|
|
14524
14770
|
import {
|
|
14525
|
-
closeSync as
|
|
14771
|
+
closeSync as closeSync8,
|
|
14526
14772
|
existsSync as existsSync6,
|
|
14527
|
-
fstatSync as
|
|
14773
|
+
fstatSync as fstatSync3,
|
|
14528
14774
|
fsyncSync as fsyncSync2,
|
|
14529
14775
|
lstatSync,
|
|
14530
14776
|
mkdirSync as mkdirSync6,
|
|
14531
|
-
openSync as
|
|
14532
|
-
readFileSync as
|
|
14777
|
+
openSync as openSync8,
|
|
14778
|
+
readFileSync as readFileSync11,
|
|
14533
14779
|
renameSync as renameSync3,
|
|
14534
14780
|
unlinkSync as unlinkSync4,
|
|
14535
14781
|
writeFileSync as writeFileSync6
|
|
14536
14782
|
} from "fs";
|
|
14537
|
-
import { dirname as
|
|
14783
|
+
import { dirname as dirname6 } from "path";
|
|
14538
14784
|
var JOURNAL_SCHEMA_VERSION = 1;
|
|
14539
14785
|
var DIR_MODE2 = 448;
|
|
14540
14786
|
var FILE_MODE4 = 384;
|
|
@@ -14620,8 +14866,8 @@ function readJournalUnlocked(path) {
|
|
|
14620
14866
|
if (before.isSymbolicLink() || !before.isFile()) {
|
|
14621
14867
|
throw new Error("Credential cleanup journal must be a regular file.");
|
|
14622
14868
|
}
|
|
14623
|
-
fd =
|
|
14624
|
-
const opened =
|
|
14869
|
+
fd = openSync8(path, "r");
|
|
14870
|
+
const opened = fstatSync3(fd);
|
|
14625
14871
|
if (before.dev !== opened.dev || before.ino !== opened.ino) {
|
|
14626
14872
|
throw new Error("Credential cleanup journal changed while opening.");
|
|
14627
14873
|
}
|
|
@@ -14636,44 +14882,44 @@ function readJournalUnlocked(path) {
|
|
|
14636
14882
|
if (opened.size > MAX_JOURNAL_BYTES) {
|
|
14637
14883
|
throw new Error("Credential cleanup journal is too large.");
|
|
14638
14884
|
}
|
|
14639
|
-
return parseJournal(JSON.parse(
|
|
14885
|
+
return parseJournal(JSON.parse(readFileSync11(fd, "utf8")));
|
|
14640
14886
|
} catch (error) {
|
|
14641
14887
|
const message = error instanceof Error ? error.message : String(error);
|
|
14642
14888
|
throw new Error(`Could not read credential cleanup journal: ${message}`);
|
|
14643
14889
|
} finally {
|
|
14644
|
-
if (fd !== void 0)
|
|
14890
|
+
if (fd !== void 0) closeSync8(fd);
|
|
14645
14891
|
}
|
|
14646
14892
|
}
|
|
14647
14893
|
function syncParentDirectory(path) {
|
|
14648
14894
|
let fd;
|
|
14649
14895
|
try {
|
|
14650
|
-
fd =
|
|
14896
|
+
fd = openSync8(dirname6(path), "r");
|
|
14651
14897
|
fsyncSync2(fd);
|
|
14652
14898
|
} catch (error) {
|
|
14653
14899
|
const code = error.code;
|
|
14654
14900
|
if (code !== "EINVAL" && code !== "ENOTSUP" && code !== "EPERM") throw error;
|
|
14655
14901
|
} finally {
|
|
14656
|
-
if (fd !== void 0)
|
|
14902
|
+
if (fd !== void 0) closeSync8(fd);
|
|
14657
14903
|
}
|
|
14658
14904
|
}
|
|
14659
14905
|
function writeJournalUnlocked(journal, path) {
|
|
14660
14906
|
assertRegistryWriteOwnership(path);
|
|
14661
14907
|
ensureSecureAppHome();
|
|
14662
|
-
mkdirSync6(
|
|
14908
|
+
mkdirSync6(dirname6(path), { recursive: true, mode: DIR_MODE2 });
|
|
14663
14909
|
const tmp = `${path}.${process.pid}.${randomUUID4()}.tmp`;
|
|
14664
14910
|
let fd;
|
|
14665
14911
|
try {
|
|
14666
|
-
fd =
|
|
14912
|
+
fd = openSync8(tmp, "wx", FILE_MODE4);
|
|
14667
14913
|
writeFileSync6(fd, `${JSON.stringify(journal, null, 2)}
|
|
14668
14914
|
`);
|
|
14669
14915
|
fsyncSync2(fd);
|
|
14670
|
-
|
|
14916
|
+
closeSync8(fd);
|
|
14671
14917
|
fd = void 0;
|
|
14672
14918
|
assertRegistryWriteOwnership(path);
|
|
14673
14919
|
renameSync3(tmp, path);
|
|
14674
14920
|
syncParentDirectory(path);
|
|
14675
14921
|
} finally {
|
|
14676
|
-
if (fd !== void 0)
|
|
14922
|
+
if (fd !== void 0) closeSync8(fd);
|
|
14677
14923
|
try {
|
|
14678
14924
|
unlinkSync4(tmp);
|
|
14679
14925
|
} catch (error) {
|
|
@@ -18642,8 +18888,8 @@ import { createBrotliDecompress, createGunzip, createInflate } from "zlib";
|
|
|
18642
18888
|
|
|
18643
18889
|
// src/http-proxy/ca.ts
|
|
18644
18890
|
import { randomBytes as randomBytes2 } from "crypto";
|
|
18645
|
-
import { chmodSync as chmodSync4, existsSync as existsSync7, mkdirSync as mkdirSync7, readFileSync as
|
|
18646
|
-
import { dirname as
|
|
18891
|
+
import { chmodSync as chmodSync4, existsSync as existsSync7, mkdirSync as mkdirSync7, readFileSync as readFileSync12, writeFileSync as writeFileSync7 } from "fs";
|
|
18892
|
+
import { dirname as dirname7, join as join9, resolve as resolve3 } from "path";
|
|
18647
18893
|
import forge from "node-forge";
|
|
18648
18894
|
var CERT_DIR = "http-proxy";
|
|
18649
18895
|
var CA_CERT_FILE = "clodex-ca.pem";
|
|
@@ -18658,14 +18904,14 @@ function serialNumber() {
|
|
|
18658
18904
|
return bytes.toString("hex");
|
|
18659
18905
|
}
|
|
18660
18906
|
function certPaths() {
|
|
18661
|
-
const dir =
|
|
18907
|
+
const dir = join9(getAppHome(), CERT_DIR);
|
|
18662
18908
|
return {
|
|
18663
18909
|
dir,
|
|
18664
|
-
caCert:
|
|
18665
|
-
caKey:
|
|
18666
|
-
serverCert:
|
|
18667
|
-
serverKey:
|
|
18668
|
-
version:
|
|
18910
|
+
caCert: join9(dir, CA_CERT_FILE),
|
|
18911
|
+
caKey: join9(dir, CA_KEY_FILE),
|
|
18912
|
+
serverCert: join9(dir, SERVER_CERT_FILE),
|
|
18913
|
+
serverKey: join9(dir, SERVER_KEY_FILE),
|
|
18914
|
+
version: join9(dir, CERT_VERSION_FILE)
|
|
18669
18915
|
};
|
|
18670
18916
|
}
|
|
18671
18917
|
function writePrivate(path, value) {
|
|
@@ -18718,8 +18964,8 @@ function generateCertificates(paths) {
|
|
|
18718
18964
|
}
|
|
18719
18965
|
function storedCertificatesAreCurrent(paths) {
|
|
18720
18966
|
try {
|
|
18721
|
-
const ca = forge.pki.certificateFromPem(
|
|
18722
|
-
const server = forge.pki.certificateFromPem(
|
|
18967
|
+
const ca = forge.pki.certificateFromPem(readFileSync12(paths.caCert, "utf8"));
|
|
18968
|
+
const server = forge.pki.certificateFromPem(readFileSync12(paths.serverCert, "utf8"));
|
|
18723
18969
|
const now = Date.now();
|
|
18724
18970
|
const renewalBuffer = 7 * 24 * 60 * 60 * 1e3;
|
|
18725
18971
|
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);
|
|
@@ -18730,13 +18976,13 @@ function storedCertificatesAreCurrent(paths) {
|
|
|
18730
18976
|
function ensureHttpProxyCertificates() {
|
|
18731
18977
|
const paths = certPaths();
|
|
18732
18978
|
const required = [paths.caCert, paths.caKey, paths.serverCert, paths.serverKey, paths.version];
|
|
18733
|
-
const current = required.every(existsSync7) &&
|
|
18979
|
+
const current = required.every(existsSync7) && readFileSync12(paths.version, "utf8") === CERT_VERSION && storedCertificatesAreCurrent(paths);
|
|
18734
18980
|
if (!current) generateCertificates(paths);
|
|
18735
18981
|
return {
|
|
18736
18982
|
caCertPath: paths.caCert,
|
|
18737
|
-
caCert:
|
|
18738
|
-
serverCert:
|
|
18739
|
-
serverKey:
|
|
18983
|
+
caCert: readFileSync12(paths.caCert, "utf8"),
|
|
18984
|
+
serverCert: readFileSync12(paths.serverCert, "utf8"),
|
|
18985
|
+
serverKey: readFileSync12(paths.serverKey, "utf8")
|
|
18740
18986
|
};
|
|
18741
18987
|
}
|
|
18742
18988
|
function ensureHttpProxyCaBundle(relayCaCertPath, additionalCaCertPath, onWarning) {
|
|
@@ -18750,7 +18996,7 @@ function ensureHttpProxyCaBundle(relayCaCertPath, additionalCaCertPath, onWarnin
|
|
|
18750
18996
|
let additionalCa;
|
|
18751
18997
|
try {
|
|
18752
18998
|
if (resolve3(additionalCaCertPath) === resolve3(relayCaCertPath)) return relayCaCertPath;
|
|
18753
|
-
additionalCa =
|
|
18999
|
+
additionalCa = readFileSync12(additionalCaCertPath, "utf8").trim();
|
|
18754
19000
|
} catch (err) {
|
|
18755
19001
|
return warn(
|
|
18756
19002
|
`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.`
|
|
@@ -18760,15 +19006,15 @@ function ensureHttpProxyCaBundle(relayCaCertPath, additionalCaCertPath, onWarnin
|
|
|
18760
19006
|
return warn(`NODE_EXTRA_CA_CERTS=${additionalCaCertPath} is empty, so it adds nothing.`);
|
|
18761
19007
|
}
|
|
18762
19008
|
try {
|
|
18763
|
-
const relayCa =
|
|
18764
|
-
const combinedPath =
|
|
19009
|
+
const relayCa = readFileSync12(relayCaCertPath, "utf8").trimEnd();
|
|
19010
|
+
const combinedPath = join9(dirname7(relayCaCertPath), "combined-ca.pem");
|
|
18765
19011
|
writePublic(combinedPath, `${relayCa}
|
|
18766
19012
|
${additionalCa}
|
|
18767
19013
|
`);
|
|
18768
19014
|
return combinedPath;
|
|
18769
19015
|
} catch (err) {
|
|
18770
19016
|
return warn(
|
|
18771
|
-
`clodex could not build the combined CA bundle in ${
|
|
19017
|
+
`clodex could not build the combined CA bundle in ${dirname7(relayCaCertPath)} (${err instanceof Error ? err.message : String(err)}), so the readable, non-empty NODE_EXTRA_CA_CERTS=${additionalCaCertPath} was left out of it. Fix the reported error and restart clodex.`
|
|
18772
19018
|
);
|
|
18773
19019
|
}
|
|
18774
19020
|
}
|