@bman654/clodex 2.11.5 → 2.11.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -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.5",
385
+ version: "2.11.6",
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 readFileSync9,
3891
+ readFileSync as readFileSync10,
3892
3892
  renameSync as renameSync2,
3893
3893
  rmSync,
3894
- statSync as statSync7,
3894
+ statSync as statSync8,
3895
3895
  unlinkSync as unlinkSync3,
3896
3896
  writeFileSync as writeFileSync5,
3897
- openSync as openSync6,
3898
- closeSync as closeSync6,
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 dirname4, join as join7 } from "path";
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 closeSync2, openSync as openSync2, readSync, statSync as statSync5, writeSync as writeSync2 } from "fs";
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 = readSync(fd, buffer, 0, length, position);
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 = openSync2(path, "r");
7250
+ const fd = openSync3(path, "r");
7156
7251
  try {
7157
- return readBunModuleNames(fd, statSync5(path).size);
7252
+ return readBunModuleNames(fd, statSync6(path).size);
7158
7253
  } finally {
7159
- closeSync2(fd);
7254
+ closeSync3(fd);
7160
7255
  }
7161
7256
  }
7162
7257
  function readBunJavaScriptModules(path) {
7163
- const fd = openSync2(path, "r");
7258
+ const fd = openSync3(path, "r");
7164
7259
  try {
7165
- const parsed = readBunModuleNames(fd, statSync5(path).size);
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
- closeSync2(fd);
7280
+ closeSync3(fd);
7186
7281
  }
7187
7282
  }
7188
7283
  function shimEntryModuleName(path) {
7189
- const fd = openSync2(path, "r+");
7284
+ const fd = openSync3(path, "r+");
7190
7285
  try {
7191
- const parsed = readBunModuleNames(fd, statSync5(path).size);
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, statSync5(path).size, marker).some((offset2) => isModuleNameAt(fd, offset2, marker))) {
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
- closeSync2(fd);
7299
+ closeSync3(fd);
7205
7300
  }
7206
7301
  }
7207
7302
  function restoreEntryModuleName(path, shim, { resign }) {
7208
- const fd = openSync2(path, "r+");
7303
+ const fd = openSync3(path, "r+");
7209
7304
  let machO;
7210
7305
  try {
7211
- const parsed = readBunModuleNames(fd, statSync5(path).size);
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, statSync5(path).size, shim);
7314
+ restoreEveryStandIn(fd, statSync6(path).size, shim);
7220
7315
  machO = resign;
7221
7316
  } finally {
7222
- closeSync2(fd);
7317
+ closeSync3(fd);
7223
7318
  }
7224
7319
  if (machO) resignMachOBinary(path);
7225
7320
  }
7226
7321
  function resignMachOBinary(path) {
7227
- const fd = openSync2(path, "r");
7322
+ const fd = openSync3(path, "r");
7228
7323
  let machO;
7229
7324
  try {
7230
7325
  machO = isMachO(fd);
7231
7326
  } finally {
7232
- closeSync2(fd);
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 closeSync3, fstatSync, openSync as openSync3, readSync as readSync2, writeSync as writeSync3 } from "fs";
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 = readSync2(fd, buf, read, length - read, position + read);
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 = fstatSync(fd).size;
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 = openSync3(path, "r+");
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
- closeSync3(fd);
7506
+ closeSync4(fd);
7412
7507
  }
7413
7508
  }
7414
7509
  function restoreBunCompiledPointer(path, shim) {
7415
- const fd = openSync3(path, "r+");
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
- closeSync3(fd);
7547
+ closeSync4(fd);
7453
7548
  }
7454
7549
  }
7455
7550
 
7456
7551
  // src/bun-bundle.ts
7457
- import { closeSync as closeSync4, openSync as openSync4, readSync as readSync3, writeSync as writeSync4 } from "fs";
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 = openSync4(path, "r");
7668
+ const fd = openSync5(path, "r");
7574
7669
  try {
7575
7670
  let read = 0;
7576
7671
  while (read < table.byteCount) {
7577
- const got = readSync3(fd, into, read, table.byteCount - read, table.blobAt + read);
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
- closeSync4(fd);
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 = openSync4(path, "r+");
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
- closeSync4(fd);
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 readFileSync8, readdirSync, statSync as statSync6 } from "fs";
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 join6 } from "path";
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() || join6(homedir2(), ".tweakcc");
7780
+ return process.env["TWEAKCC_CONFIG_DIR"]?.trim() || join7(homedir2(), ".tweakcc");
7686
7781
  }
7687
7782
  function sha256File(path) {
7688
- return createHash5("sha256").update(readFileSync8(path)).digest("hex");
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 join6(dir, `claude-${backupVersionTag(version)}-${sha256.slice(0, BACKUP_SHA_PREFIX_LENGTH)}.orig`);
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 join6(dir, "native-binary.backup");
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 = join6(dir, entry);
7810
+ const path = join7(dir, entry);
7716
7811
  let sha256;
7717
7812
  try {
7718
- if (!statSync6(path).isFile()) continue;
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 manifest = facts.manifest && facts.manifest.binaryPath === facts.binaryPath ? facts.manifest : null;
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
- if (!chosen) {
7763
- notes.push(`Recorded pristine backup ${manifest.backupPath} is missing, corrupt, or belongs to another claude version \u2014 ignoring it.`);
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. Remove the wrong one (or reinstall Claude Code), then run \`clodex patch\`.`
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 {
@@ -11143,7 +11261,7 @@ function thinkingProviderOptions(npm) {
11143
11261
 
11144
11262
  // src/proxy.ts
11145
11263
  import { createServer } from "http";
11146
- import { appendFileSync as appendFileSync2, openSync as openSync5, writeSync as writeSync5, closeSync as closeSync5 } from "fs";
11264
+ import { appendFileSync as appendFileSync2, openSync as openSync6, writeSync as writeSync5, closeSync as closeSync6 } from "fs";
11147
11265
 
11148
11266
  // src/http-utils.ts
11149
11267
  import * as zlib from "zlib";
@@ -11709,45 +11827,125 @@ function resolveUpstreamTools(tools, messages) {
11709
11827
 
11710
11828
  // src/tool-schema-sanitize.ts
11711
11829
  function isCompatiblePattern(pattern) {
11830
+ let inClass = false;
11712
11831
  for (let i = 0; i < pattern.length; i++) {
11713
- if (pattern[i] === "\\") {
11832
+ const ch = pattern[i];
11833
+ if (ch === "\\") {
11714
11834
  const next = pattern[i + 1];
11715
- if ((next === "p" || next === "P") && pattern[i + 2] === "{") return false;
11835
+ if ((next === "p" || next === "P" || next === "u" || next === "x") && pattern[i + 2] === "{") return false;
11716
11836
  if (next === "k" && pattern[i + 2] === "<") return false;
11717
11837
  i++;
11718
11838
  continue;
11719
11839
  }
11720
- if (pattern[i] === "(" && pattern[i + 1] === "?" && pattern[i + 2] === "<" && pattern[i + 3] !== "=" && pattern[i + 3] !== "!") return false;
11840
+ if (inClass) {
11841
+ if (ch === "]") inClass = false;
11842
+ continue;
11843
+ }
11844
+ if (ch === "[") {
11845
+ inClass = true;
11846
+ continue;
11847
+ }
11848
+ if (ch === "(" && pattern[i + 1] === "?" && pattern[i + 2] === "<" && pattern[i + 3] !== "=" && pattern[i + 3] !== "!") return false;
11721
11849
  }
11722
11850
  return true;
11723
11851
  }
11852
+ var SUBSCHEMA_KEYWORDS = /* @__PURE__ */ new Set([
11853
+ "items",
11854
+ "prefixItems",
11855
+ "additionalItems",
11856
+ "unevaluatedItems",
11857
+ "additionalProperties",
11858
+ "unevaluatedProperties",
11859
+ "propertyNames",
11860
+ "contains",
11861
+ "anyOf",
11862
+ "oneOf",
11863
+ "allOf",
11864
+ "not",
11865
+ "if",
11866
+ "then",
11867
+ "else",
11868
+ "contentSchema"
11869
+ ]);
11870
+ var SUBSCHEMA_MAP_KEYWORDS = /* @__PURE__ */ new Set([
11871
+ "properties",
11872
+ "$defs",
11873
+ "definitions",
11874
+ "dependentSchemas",
11875
+ "dependencies"
11876
+ ]);
11724
11877
  function sanitizeToolSchema(schema) {
11725
- if (Array.isArray(schema)) {
11878
+ return sanitizeSchema(schema);
11879
+ }
11880
+ function sanitizeSchema(node) {
11881
+ if (Array.isArray(node)) {
11726
11882
  let changed2 = false;
11727
- const out2 = schema.map((entry) => {
11728
- const next = sanitizeToolSchema(entry);
11883
+ const out = node.map((entry) => {
11884
+ const next = sanitizeSchema(entry);
11729
11885
  changed2 ||= next !== entry;
11730
11886
  return next;
11731
11887
  });
11732
- return changed2 ? out2 : schema;
11888
+ return changed2 ? out : node;
11733
11889
  }
11734
- if (!schema || typeof schema !== "object") return schema;
11890
+ if (!node || typeof node !== "object") return node;
11891
+ const record = node;
11892
+ const patternProps = "patternProperties" in record ? sanitizePatternProperties(record.patternProperties) : void 0;
11893
+ const openClosure = patternProps?.dropped === true;
11735
11894
  let changed = false;
11736
- const out = {};
11737
- for (const [key, value] of Object.entries(schema)) {
11738
- if (key === "pattern" && typeof value === "string") {
11739
- if (!isCompatiblePattern(value)) {
11895
+ const entries = [];
11896
+ for (const [key, value] of Object.entries(record)) {
11897
+ if (key === "patternProperties" && patternProps) {
11898
+ changed ||= patternProps.value !== value;
11899
+ entries.push([key, patternProps.value]);
11900
+ continue;
11901
+ }
11902
+ if (openClosure && value !== true && (key === "additionalProperties" || key === "unevaluatedProperties")) {
11903
+ changed = true;
11904
+ continue;
11905
+ }
11906
+ if (key === "pattern") {
11907
+ if (typeof value === "string" && !isCompatiblePattern(value)) {
11740
11908
  changed = true;
11741
11909
  continue;
11742
11910
  }
11743
- out[key] = value;
11911
+ entries.push([key, value]);
11744
11912
  continue;
11745
11913
  }
11746
- const next = sanitizeToolSchema(value);
11914
+ let next = value;
11915
+ if (SUBSCHEMA_KEYWORDS.has(key)) next = sanitizeSchema(value);
11916
+ else if (SUBSCHEMA_MAP_KEYWORDS.has(key)) next = sanitizeSchemaMap(value);
11747
11917
  changed ||= next !== value;
11748
- out[key] = next;
11918
+ entries.push([key, next]);
11749
11919
  }
11750
- return changed ? out : schema;
11920
+ return changed ? Object.fromEntries(entries) : node;
11921
+ }
11922
+ function sanitizeSchemaMap(node) {
11923
+ if (!node || typeof node !== "object" || Array.isArray(node)) return node;
11924
+ let changed = false;
11925
+ const entries = [];
11926
+ for (const [key, value] of Object.entries(node)) {
11927
+ const next = sanitizeSchema(value);
11928
+ changed ||= next !== value;
11929
+ entries.push([key, next]);
11930
+ }
11931
+ return changed ? Object.fromEntries(entries) : node;
11932
+ }
11933
+ function sanitizePatternProperties(node) {
11934
+ if (!node || typeof node !== "object" || Array.isArray(node)) return { value: node, dropped: false };
11935
+ let changed = false;
11936
+ let dropped = false;
11937
+ const entries = [];
11938
+ for (const [key, value] of Object.entries(node)) {
11939
+ if (!isCompatiblePattern(key)) {
11940
+ changed = true;
11941
+ dropped = true;
11942
+ continue;
11943
+ }
11944
+ const next = sanitizeSchema(value);
11945
+ changed ||= next !== value;
11946
+ entries.push([key, next]);
11947
+ }
11948
+ return { value: changed ? Object.fromEntries(entries) : node, dropped };
11751
11949
  }
11752
11950
 
11753
11951
  // src/upstream-attempts.ts
@@ -12849,12 +13047,12 @@ function createTranslationLifecycle(logPath, requestId, claudeSessionId, modelId
12849
13047
  function appendSecureLog(logPath, line) {
12850
13048
  const redacted = redactTraceLine(line);
12851
13049
  try {
12852
- const fd = openSync5(logPath, "a", 384);
13050
+ const fd = openSync6(logPath, "a", 384);
12853
13051
  try {
12854
13052
  writeSync5(fd, `${(/* @__PURE__ */ new Date()).toISOString()} ${redacted}
12855
13053
  `);
12856
13054
  } finally {
12857
- closeSync5(fd);
13055
+ closeSync6(fd);
12858
13056
  }
12859
13057
  } catch {
12860
13058
  try {
@@ -13538,14 +13736,14 @@ function buildHttpProxyRoutes(providers, favorites, modelAliases = void 0, max =
13538
13736
 
13539
13737
  // src/patcher.ts
13540
13738
  function getPatchManifestPath() {
13541
- return join7(getAppHome(), "patch-state.json");
13739
+ return join8(getAppHome(), "patch-state.json");
13542
13740
  }
13543
13741
  function getPatchLockPath() {
13544
- return join7(getAppHome(), "patch.lock");
13742
+ return join8(getAppHome(), "patch.lock");
13545
13743
  }
13546
13744
  function readPatchManifest(path = getPatchManifestPath()) {
13547
13745
  try {
13548
- const parsed = JSON.parse(readFileSync9(path, "utf8"));
13746
+ const parsed = JSON.parse(readFileSync10(path, "utf8"));
13549
13747
  if (parsed && typeof parsed.binaryPath === "string" && typeof parsed.configHash === "string") {
13550
13748
  return parsed;
13551
13749
  }
@@ -13708,13 +13906,13 @@ function pidIsAlive(pid) {
13708
13906
  function tryAcquirePatchLock(lockPath = getPatchLockPath(), opts = {}) {
13709
13907
  const now = opts.now ?? Date.now();
13710
13908
  const isAlive = opts.isAlive ?? pidIsAlive;
13711
- mkdirSync5(join7(lockPath, ".."), { recursive: true, mode: 448 });
13909
+ mkdirSync5(join8(lockPath, ".."), { recursive: true, mode: 448 });
13712
13910
  for (let attempt = 0; attempt < 2; attempt++) {
13713
13911
  try {
13714
- const fd = openSync6(lockPath, "wx");
13912
+ const fd = openSync7(lockPath, "wx");
13715
13913
  const content = { pid: process.pid, startedAt: now };
13716
13914
  writeFileSync5(fd, JSON.stringify(content));
13717
- closeSync6(fd);
13915
+ closeSync7(fd);
13718
13916
  return () => {
13719
13917
  try {
13720
13918
  unlinkSync3(lockPath);
@@ -13724,7 +13922,7 @@ function tryAcquirePatchLock(lockPath = getPatchLockPath(), opts = {}) {
13724
13922
  } catch {
13725
13923
  let stale = false;
13726
13924
  try {
13727
- const existing = JSON.parse(readFileSync9(lockPath, "utf8"));
13925
+ const existing = JSON.parse(readFileSync10(lockPath, "utf8"));
13728
13926
  stale = !existing.pid || !isAlive(existing.pid) || typeof existing.startedAt === "number" && now - existing.startedAt > PATCH_LOCK_STALE_MS;
13729
13927
  } catch {
13730
13928
  stale = true;
@@ -13740,7 +13938,7 @@ function tryAcquirePatchLock(lockPath = getPatchLockPath(), opts = {}) {
13740
13938
  }
13741
13939
  function resolveClaudeBinaryForPatch() {
13742
13940
  const envOverride = process.env["TWEAKCC_CC_INSTALLATION_PATH"];
13743
- const nativeSymlink = join7(homedir3(), ".local", "bin", "claude");
13941
+ const nativeSymlink = join8(homedir3(), ".local", "bin", "claude");
13744
13942
  const source = envOverride?.trim() || (existsSync5(nativeSymlink) ? nativeSymlink : null) || findClaudeBinary();
13745
13943
  if (!source) return { ok: false, reason: "binary-not-found" };
13746
13944
  let resolved;
@@ -13762,18 +13960,37 @@ function resolveClaudeBinaryForPatch() {
13762
13960
  }
13763
13961
  resolved = followed.path;
13764
13962
  try {
13765
- if (!statSync7(resolved).isFile()) return { ok: false, reason: "binary-not-found" };
13963
+ if (!statSync8(resolved).isFile()) return { ok: false, reason: "binary-not-found" };
13766
13964
  } catch {
13767
13965
  return { ok: false, reason: "binary-not-found" };
13768
13966
  }
13967
+ const placeholder = inspectClaudeNativeBinaryPlaceholder(resolved);
13968
+ if (placeholder) {
13969
+ return {
13970
+ ok: false,
13971
+ reason: "native-binary-missing",
13972
+ binaryPath: resolved,
13973
+ ...placeholder
13974
+ };
13975
+ }
13769
13976
  const version = getClaudeVersionForBinary(resolved);
13770
13977
  if (!version) return { ok: false, reason: "version-unknown", binaryPath: resolved };
13771
13978
  return { ok: true, binaryPath: resolved, version };
13772
13979
  }
13773
- function describePatchTargetFailure(target) {
13980
+ function installScriptCommand(path) {
13981
+ return path === null ? "`node node_modules/@anthropic-ai/claude-code/install.cjs` (adjust the path for a local or global install)" : `\`node "${path}"\``;
13982
+ }
13983
+ function describePatchTargetFailure(target, command = "patch") {
13774
13984
  if (target.reason === "launcher-unresolved") {
13775
13985
  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
13986
  }
13987
+ if (target.reason === "native-binary-missing") {
13988
+ const installer = installScriptCommand(target.installScriptPath);
13989
+ 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\`.`;
13990
+ const retry = command === "restore" ? "Then run `clodex patch --restore` again." : command === "launch" ? "Then run the command again." : "Then run `clodex patch` again.";
13991
+ const restore = command === "restore" ? ` If you need to restore by hand, pristine backups are in ${backupDir()}.` : "";
13992
+ 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.`;
13993
+ }
13777
13994
  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
13995
  }
13779
13996
  function summarizePatchResults(results) {
@@ -13870,8 +14087,8 @@ async function applyPatch(binaryPath, version, desired, configHash, opts) {
13870
14087
  try {
13871
14088
  mkdirSync5(backupDir(), { recursive: true });
13872
14089
  const { tryDetectInstallation, readContent, writeContent } = await import("tweakcc");
13873
- candidateDir = mkdtempSync(join7(dirname4(binaryPath), ".clodex-patch-"));
13874
- const candidatePath = join7(candidateDir, basename(binaryPath));
14090
+ candidateDir = mkdtempSync(join8(dirname5(binaryPath), ".clodex-patch-"));
14091
+ const candidatePath = join8(candidateDir, basename(binaryPath));
13875
14092
  const seedCandidate = async (from) => {
13876
14093
  copyFileSync(from, candidatePath);
13877
14094
  const shim = shimEntryModuleName(candidatePath);
@@ -14001,7 +14218,7 @@ async function applyPatch(binaryPath, version, desired, configHash, opts) {
14001
14218
  }
14002
14219
  if (writeShim) restoreEntryModuleName(candidatePath, writeShim, { resign: true });
14003
14220
  else if (publishedBlob) resignMachOBinary(candidatePath);
14004
- patchedSize = statSync7(candidatePath).size;
14221
+ patchedSize = statSync8(candidatePath).size;
14005
14222
  patchedSha256 = sha256File(candidatePath);
14006
14223
  renameSync2(candidatePath, binaryPath);
14007
14224
  } catch (err) {
@@ -14054,12 +14271,12 @@ async function applyPatch(binaryPath, version, desired, configHash, opts) {
14054
14271
  };
14055
14272
  }
14056
14273
  function runRestoreCommand(target) {
14057
- if (!target.ok && target.reason === "binary-not-found") {
14058
- p2.log.error(describePatchTargetFailure(target));
14274
+ if (!target.ok && (target.reason === "binary-not-found" || target.reason === "native-binary-missing")) {
14275
+ p2.log.error(describePatchTargetFailure(target, "restore"));
14059
14276
  return 1;
14060
14277
  }
14061
14278
  if (!target.ok && target.reason === "launcher-unresolved" && target.declaredTarget === null) {
14062
- p2.log.error(describePatchTargetFailure(target));
14279
+ p2.log.error(describePatchTargetFailure(target, "restore"));
14063
14280
  return 1;
14064
14281
  }
14065
14282
  const binaryPath = target.binaryPath;
@@ -14130,7 +14347,7 @@ async function runPatchCommand(opts = {}) {
14130
14347
  binaryPath,
14131
14348
  claudeVersion: version,
14132
14349
  configHash,
14133
- binarySize: statSync7(binaryPath).size
14350
+ binarySize: statSync8(binaryPath).size
14134
14351
  });
14135
14352
  if (state === "current") {
14136
14353
  p2.log.success(`claude ${version} is already patched with the current model config \u2014 nothing to do.`);
@@ -14168,7 +14385,7 @@ async function runLaunchPatchCheck(opts = {}) {
14168
14385
  const target = resolveClaudeBinaryForPatch();
14169
14386
  if (!target.ok) {
14170
14387
  if (target.reason !== "binary-not-found" && !opts.agentStdout) {
14171
- console.error(pc3.dim(`clodex: ${describePatchTargetFailure(target)}`));
14388
+ console.error(pc3.dim(`clodex: ${describePatchTargetFailure(target, "launch")}`));
14172
14389
  }
14173
14390
  return;
14174
14391
  }
@@ -14186,7 +14403,7 @@ async function runLaunchPatchCheck(opts = {}) {
14186
14403
  binaryPath: resolved.binaryPath,
14187
14404
  claudeVersion: resolved.version,
14188
14405
  configHash,
14189
- binarySize: statSync7(resolved.binaryPath).size
14406
+ binarySize: statSync8(resolved.binaryPath).size
14190
14407
  });
14191
14408
  if (state === "current") return;
14192
14409
  const interactive = !opts.dryRun && !opts.agentStdout && process.stdin.isTTY === true && process.stdout.isTTY === true;
@@ -14522,19 +14739,19 @@ function localProvidersToServerModels(localProviders) {
14522
14739
  // src/registry/credential-cleanup-journal.ts
14523
14740
  import { randomUUID as randomUUID4 } from "crypto";
14524
14741
  import {
14525
- closeSync as closeSync7,
14742
+ closeSync as closeSync8,
14526
14743
  existsSync as existsSync6,
14527
- fstatSync as fstatSync2,
14744
+ fstatSync as fstatSync3,
14528
14745
  fsyncSync as fsyncSync2,
14529
14746
  lstatSync,
14530
14747
  mkdirSync as mkdirSync6,
14531
- openSync as openSync7,
14532
- readFileSync as readFileSync10,
14748
+ openSync as openSync8,
14749
+ readFileSync as readFileSync11,
14533
14750
  renameSync as renameSync3,
14534
14751
  unlinkSync as unlinkSync4,
14535
14752
  writeFileSync as writeFileSync6
14536
14753
  } from "fs";
14537
- import { dirname as dirname5 } from "path";
14754
+ import { dirname as dirname6 } from "path";
14538
14755
  var JOURNAL_SCHEMA_VERSION = 1;
14539
14756
  var DIR_MODE2 = 448;
14540
14757
  var FILE_MODE4 = 384;
@@ -14620,8 +14837,8 @@ function readJournalUnlocked(path) {
14620
14837
  if (before.isSymbolicLink() || !before.isFile()) {
14621
14838
  throw new Error("Credential cleanup journal must be a regular file.");
14622
14839
  }
14623
- fd = openSync7(path, "r");
14624
- const opened = fstatSync2(fd);
14840
+ fd = openSync8(path, "r");
14841
+ const opened = fstatSync3(fd);
14625
14842
  if (before.dev !== opened.dev || before.ino !== opened.ino) {
14626
14843
  throw new Error("Credential cleanup journal changed while opening.");
14627
14844
  }
@@ -14636,44 +14853,44 @@ function readJournalUnlocked(path) {
14636
14853
  if (opened.size > MAX_JOURNAL_BYTES) {
14637
14854
  throw new Error("Credential cleanup journal is too large.");
14638
14855
  }
14639
- return parseJournal(JSON.parse(readFileSync10(fd, "utf8")));
14856
+ return parseJournal(JSON.parse(readFileSync11(fd, "utf8")));
14640
14857
  } catch (error) {
14641
14858
  const message = error instanceof Error ? error.message : String(error);
14642
14859
  throw new Error(`Could not read credential cleanup journal: ${message}`);
14643
14860
  } finally {
14644
- if (fd !== void 0) closeSync7(fd);
14861
+ if (fd !== void 0) closeSync8(fd);
14645
14862
  }
14646
14863
  }
14647
14864
  function syncParentDirectory(path) {
14648
14865
  let fd;
14649
14866
  try {
14650
- fd = openSync7(dirname5(path), "r");
14867
+ fd = openSync8(dirname6(path), "r");
14651
14868
  fsyncSync2(fd);
14652
14869
  } catch (error) {
14653
14870
  const code = error.code;
14654
14871
  if (code !== "EINVAL" && code !== "ENOTSUP" && code !== "EPERM") throw error;
14655
14872
  } finally {
14656
- if (fd !== void 0) closeSync7(fd);
14873
+ if (fd !== void 0) closeSync8(fd);
14657
14874
  }
14658
14875
  }
14659
14876
  function writeJournalUnlocked(journal, path) {
14660
14877
  assertRegistryWriteOwnership(path);
14661
14878
  ensureSecureAppHome();
14662
- mkdirSync6(dirname5(path), { recursive: true, mode: DIR_MODE2 });
14879
+ mkdirSync6(dirname6(path), { recursive: true, mode: DIR_MODE2 });
14663
14880
  const tmp = `${path}.${process.pid}.${randomUUID4()}.tmp`;
14664
14881
  let fd;
14665
14882
  try {
14666
- fd = openSync7(tmp, "wx", FILE_MODE4);
14883
+ fd = openSync8(tmp, "wx", FILE_MODE4);
14667
14884
  writeFileSync6(fd, `${JSON.stringify(journal, null, 2)}
14668
14885
  `);
14669
14886
  fsyncSync2(fd);
14670
- closeSync7(fd);
14887
+ closeSync8(fd);
14671
14888
  fd = void 0;
14672
14889
  assertRegistryWriteOwnership(path);
14673
14890
  renameSync3(tmp, path);
14674
14891
  syncParentDirectory(path);
14675
14892
  } finally {
14676
- if (fd !== void 0) closeSync7(fd);
14893
+ if (fd !== void 0) closeSync8(fd);
14677
14894
  try {
14678
14895
  unlinkSync4(tmp);
14679
14896
  } catch (error) {
@@ -18642,8 +18859,8 @@ import { createBrotliDecompress, createGunzip, createInflate } from "zlib";
18642
18859
 
18643
18860
  // src/http-proxy/ca.ts
18644
18861
  import { randomBytes as randomBytes2 } from "crypto";
18645
- import { chmodSync as chmodSync4, existsSync as existsSync7, mkdirSync as mkdirSync7, readFileSync as readFileSync11, writeFileSync as writeFileSync7 } from "fs";
18646
- import { dirname as dirname6, join as join8, resolve as resolve3 } from "path";
18862
+ import { chmodSync as chmodSync4, existsSync as existsSync7, mkdirSync as mkdirSync7, readFileSync as readFileSync12, writeFileSync as writeFileSync7 } from "fs";
18863
+ import { dirname as dirname7, join as join9, resolve as resolve3 } from "path";
18647
18864
  import forge from "node-forge";
18648
18865
  var CERT_DIR = "http-proxy";
18649
18866
  var CA_CERT_FILE = "clodex-ca.pem";
@@ -18658,14 +18875,14 @@ function serialNumber() {
18658
18875
  return bytes.toString("hex");
18659
18876
  }
18660
18877
  function certPaths() {
18661
- const dir = join8(getAppHome(), CERT_DIR);
18878
+ const dir = join9(getAppHome(), CERT_DIR);
18662
18879
  return {
18663
18880
  dir,
18664
- caCert: join8(dir, CA_CERT_FILE),
18665
- caKey: join8(dir, CA_KEY_FILE),
18666
- serverCert: join8(dir, SERVER_CERT_FILE),
18667
- serverKey: join8(dir, SERVER_KEY_FILE),
18668
- version: join8(dir, CERT_VERSION_FILE)
18881
+ caCert: join9(dir, CA_CERT_FILE),
18882
+ caKey: join9(dir, CA_KEY_FILE),
18883
+ serverCert: join9(dir, SERVER_CERT_FILE),
18884
+ serverKey: join9(dir, SERVER_KEY_FILE),
18885
+ version: join9(dir, CERT_VERSION_FILE)
18669
18886
  };
18670
18887
  }
18671
18888
  function writePrivate(path, value) {
@@ -18718,8 +18935,8 @@ function generateCertificates(paths) {
18718
18935
  }
18719
18936
  function storedCertificatesAreCurrent(paths) {
18720
18937
  try {
18721
- const ca = forge.pki.certificateFromPem(readFileSync11(paths.caCert, "utf8"));
18722
- const server = forge.pki.certificateFromPem(readFileSync11(paths.serverCert, "utf8"));
18938
+ const ca = forge.pki.certificateFromPem(readFileSync12(paths.caCert, "utf8"));
18939
+ const server = forge.pki.certificateFromPem(readFileSync12(paths.serverCert, "utf8"));
18723
18940
  const now = Date.now();
18724
18941
  const renewalBuffer = 7 * 24 * 60 * 60 * 1e3;
18725
18942
  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 +18947,13 @@ function storedCertificatesAreCurrent(paths) {
18730
18947
  function ensureHttpProxyCertificates() {
18731
18948
  const paths = certPaths();
18732
18949
  const required = [paths.caCert, paths.caKey, paths.serverCert, paths.serverKey, paths.version];
18733
- const current = required.every(existsSync7) && readFileSync11(paths.version, "utf8") === CERT_VERSION && storedCertificatesAreCurrent(paths);
18950
+ const current = required.every(existsSync7) && readFileSync12(paths.version, "utf8") === CERT_VERSION && storedCertificatesAreCurrent(paths);
18734
18951
  if (!current) generateCertificates(paths);
18735
18952
  return {
18736
18953
  caCertPath: paths.caCert,
18737
- caCert: readFileSync11(paths.caCert, "utf8"),
18738
- serverCert: readFileSync11(paths.serverCert, "utf8"),
18739
- serverKey: readFileSync11(paths.serverKey, "utf8")
18954
+ caCert: readFileSync12(paths.caCert, "utf8"),
18955
+ serverCert: readFileSync12(paths.serverCert, "utf8"),
18956
+ serverKey: readFileSync12(paths.serverKey, "utf8")
18740
18957
  };
18741
18958
  }
18742
18959
  function ensureHttpProxyCaBundle(relayCaCertPath, additionalCaCertPath, onWarning) {
@@ -18750,7 +18967,7 @@ function ensureHttpProxyCaBundle(relayCaCertPath, additionalCaCertPath, onWarnin
18750
18967
  let additionalCa;
18751
18968
  try {
18752
18969
  if (resolve3(additionalCaCertPath) === resolve3(relayCaCertPath)) return relayCaCertPath;
18753
- additionalCa = readFileSync11(additionalCaCertPath, "utf8").trim();
18970
+ additionalCa = readFileSync12(additionalCaCertPath, "utf8").trim();
18754
18971
  } catch (err) {
18755
18972
  return warn(
18756
18973
  `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 +18977,15 @@ function ensureHttpProxyCaBundle(relayCaCertPath, additionalCaCertPath, onWarnin
18760
18977
  return warn(`NODE_EXTRA_CA_CERTS=${additionalCaCertPath} is empty, so it adds nothing.`);
18761
18978
  }
18762
18979
  try {
18763
- const relayCa = readFileSync11(relayCaCertPath, "utf8").trimEnd();
18764
- const combinedPath = join8(dirname6(relayCaCertPath), "combined-ca.pem");
18980
+ const relayCa = readFileSync12(relayCaCertPath, "utf8").trimEnd();
18981
+ const combinedPath = join9(dirname7(relayCaCertPath), "combined-ca.pem");
18765
18982
  writePublic(combinedPath, `${relayCa}
18766
18983
  ${additionalCa}
18767
18984
  `);
18768
18985
  return combinedPath;
18769
18986
  } catch (err) {
18770
18987
  return warn(
18771
- `clodex could not build the combined CA bundle in ${dirname6(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.`
18988
+ `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
18989
  );
18773
18990
  }
18774
18991
  }