@bman654/clodex 2.13.0 → 2.14.0

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
@@ -6,7 +6,11 @@ import {
6
6
  OAUTH_ACCOUNT_NAME_RE,
7
7
  REGISTRY_SCHEMA_VERSION_WITH_MATERIALIZED_ACTIVE_ACCOUNT,
8
8
  assertRegistryWriteOwnership,
9
+ backupDir,
9
10
  clearActiveOAuthAccount,
11
+ collectPristineFacts,
12
+ contentAddressedBackupPath,
13
+ emitParentNotice,
10
14
  ensureSecureAppHome,
11
15
  findClaudeBinary,
12
16
  getAppHome,
@@ -18,20 +22,32 @@ import {
18
22
  getLocalPatchesPath,
19
23
  getLogsPath,
20
24
  getOAuthAccountSlot,
25
+ getPatchManifestPath,
21
26
  getSavedServerPassword,
22
27
  getServerExposedProviders,
23
28
  getServerFavoritesOnly,
24
29
  getServerListenMode,
25
30
  getServerMaskGatewayIds,
31
+ installParentNoticeSink,
32
+ installProvenancePath,
26
33
  isDiscoveryDisabled,
34
+ isPatchedClaudeSource,
27
35
  isValidProviderId,
36
+ legacyBackupPath,
28
37
  listenTcpServer,
29
38
  loadPreferences,
30
39
  loadRegistry,
31
40
  loadRegistryStrict,
41
+ looksLikeLegacyClodexPatch,
32
42
  networkEnvBaseline,
43
+ planInspectedPristineSource,
44
+ planPristineSource,
45
+ planRestoreOnly,
33
46
  providerDefaultAuthRef,
47
+ readInstallProvenance,
34
48
  readLiveServerRuntimeStates,
49
+ readPatchManifest,
50
+ recordBackupProvenance,
35
51
  recordLaunchSelection,
36
52
  recordNetworkEnvMutation,
37
53
  registerServerRuntimeState,
@@ -44,15 +60,19 @@ import {
44
60
  setServerFavoritesOnly,
45
61
  setServerListenMode,
46
62
  setServerMaskGatewayIds,
63
+ sha256File,
47
64
  storeActiveOAuthAccount,
48
65
  tcpListenerUrlHost,
66
+ tweakccMirrorBackupPath,
49
67
  unregisterServerRuntimeState,
50
68
  waitForTcpListener,
51
69
  withCredentialMutationLock,
52
70
  withProviderMutationLock,
53
71
  withRegistryWriteLock,
54
- withRegistryWriteLockSync
55
- } from "./chunk-ZM2KYLPH.js";
72
+ withRegistryWriteLockSync,
73
+ writeParentNoticeLines,
74
+ writeParentNoticeLinesSync
75
+ } from "./chunk-3NCDMS7N.js";
56
76
 
57
77
  // src/cli.ts
58
78
  import pc13 from "picocolors";
@@ -228,69 +248,11 @@ function printFavoritesOnlyPanel() {
228
248
  // src/cli.ts
229
249
  import * as p12 from "@clack/prompts";
230
250
  import { realpathSync as realpathSync3 } from "fs";
231
- import { fileURLToPath } from "url";
251
+ import { fileURLToPath as fileURLToPath2 } from "url";
232
252
 
233
253
  // src/launch.ts
234
254
  import { spawn } from "child_process";
235
255
  import { appendFileSync } from "fs";
236
-
237
- // src/parent-notice.ts
238
- import { writeSync } from "fs";
239
- var MAX_NOTICE_CHARS = 2e3;
240
- var CONTROL_CHARS = /[\u0000-\u001f\u007f-\u009f]/g;
241
- var activeSink = null;
242
- var stderrErrorGuardInstalled = false;
243
- function toNoticeLine(message) {
244
- const flattened = message.replace(CONTROL_CHARS, " ").trimEnd();
245
- const bounded = flattened.length > MAX_NOTICE_CHARS ? `${flattened.slice(0, MAX_NOTICE_CHARS)}...` : flattened;
246
- return `${bounded}
247
- `;
248
- }
249
- function guardStderrErrors() {
250
- if (stderrErrorGuardInstalled) return;
251
- stderrErrorGuardInstalled = true;
252
- try {
253
- process.stderr.on("error", () => {
254
- });
255
- } catch {
256
- }
257
- }
258
- function writeParentNoticeLines(lines) {
259
- if (lines.length === 0) return;
260
- guardStderrErrors();
261
- try {
262
- process.stderr.write(lines.join(""), () => {
263
- });
264
- } catch {
265
- }
266
- }
267
- function writeParentNoticeLinesSync(lines) {
268
- if (lines.length === 0) return;
269
- try {
270
- writeSync(2, lines.join(""));
271
- } catch {
272
- }
273
- }
274
- function emitParentNotice(message) {
275
- const line = toNoticeLine(message);
276
- try {
277
- if (activeSink) activeSink(line);
278
- else writeParentNoticeLines([line]);
279
- } catch {
280
- }
281
- }
282
- function installParentNoticeSink(sink) {
283
- const previous = activeSink;
284
- activeSink = sink;
285
- let released = false;
286
- return () => {
287
- if (released) return;
288
- released = true;
289
- if (activeSink === sink) activeSink = previous;
290
- };
291
- }
292
-
293
- // src/launch.ts
294
256
  var isWindows = process.platform === "win32";
295
257
  var MAX_QUEUED_NOTICES = 50;
296
258
  function buildClaudeArgs(model, extraArgs) {
@@ -382,7 +344,7 @@ import { join } from "path";
382
344
  // package.json
383
345
  var package_default = {
384
346
  name: "@bman654/clodex",
385
- version: "2.13.0",
347
+ version: "2.14.0",
386
348
  publishConfig: {
387
349
  access: "public"
388
350
  },
@@ -411,6 +373,7 @@ var package_default = {
411
373
  files: [
412
374
  "dist",
413
375
  "docs",
376
+ "launcher",
414
377
  "README.md"
415
378
  ],
416
379
  engines: {
@@ -3882,25 +3845,25 @@ function applyClodexPatches(source, config) {
3882
3845
  }
3883
3846
 
3884
3847
  // src/patcher.ts
3885
- import { createHash as createHash9 } from "crypto";
3848
+ import { createHash as createHash8 } from "crypto";
3886
3849
  import {
3887
3850
  chmodSync as chmodSync4,
3888
3851
  copyFileSync,
3889
- existsSync as existsSync5,
3852
+ existsSync as existsSync4,
3890
3853
  mkdtempSync,
3891
3854
  mkdirSync as mkdirSync5,
3892
- readFileSync as readFileSync10,
3893
- renameSync as renameSync3,
3894
- rmSync as rmSync2,
3895
- statSync as statSync8,
3855
+ readFileSync as readFileSync9,
3856
+ renameSync as renameSync2,
3857
+ rmSync,
3858
+ statSync as statSync7,
3896
3859
  unlinkSync as unlinkSync3,
3897
- writeFileSync as writeFileSync6,
3860
+ writeFileSync as writeFileSync5,
3898
3861
  openSync as openSync8,
3899
3862
  closeSync as closeSync8,
3900
3863
  realpathSync as realpathSync2
3901
3864
  } from "fs";
3902
- import { homedir as homedir3 } from "os";
3903
- import { basename, dirname as dirname5, join as join8 } from "path";
3865
+ import { homedir as homedir2 } from "os";
3866
+ import { basename, dirname as dirname5, join as join7 } from "path";
3904
3867
  import pc3 from "picocolors";
3905
3868
  import * as p2 from "@clack/prompts";
3906
3869
 
@@ -7052,9 +7015,9 @@ function getPlatformKey() {
7052
7015
  return `${platform}-${cpu}`;
7053
7016
  }
7054
7017
  function inspectNativePackage(binaryPath) {
7055
- const packageRoot = dirname4(dirname4(binaryPath));
7056
- const packageJsonPath = join6(packageRoot, "package.json");
7057
- const installScriptPath = join6(packageRoot, "install.cjs");
7018
+ const packageRoot2 = dirname4(dirname4(binaryPath));
7019
+ const packageJsonPath = join6(packageRoot2, "package.json");
7020
+ const installScriptPath = join6(packageRoot2, "install.cjs");
7058
7021
  let manifest;
7059
7022
  try {
7060
7023
  manifest = JSON.parse(readFileSync8(packageJsonPath, "utf8"));
@@ -7110,7 +7073,7 @@ function inspectClaudeNativeBinaryPlaceholder(path) {
7110
7073
  }
7111
7074
 
7112
7075
  // src/bun-entry-module.ts
7113
- import { closeSync as closeSync3, openSync as openSync3, readSync as readSync2, statSync as statSync6, writeSync as writeSync2 } from "fs";
7076
+ import { closeSync as closeSync3, openSync as openSync3, readSync as readSync2, statSync as statSync6, writeSync } from "fs";
7114
7077
  import { execFileSync } from "child_process";
7115
7078
  var BUN_TRAILER = Buffer.from("\n---- Bun! ----\n");
7116
7079
  var BUN_OFFSETS_BYTES = 32;
@@ -7275,7 +7238,7 @@ function isModuleNameAt(fd, offset, marker) {
7275
7238
  }
7276
7239
  function writeNameBytes(fd, name, offset) {
7277
7240
  const bytes = Buffer.from(name);
7278
- const written = writeSync2(fd, bytes, 0, bytes.length, offset);
7241
+ const written = writeSync(fd, bytes, 0, bytes.length, offset);
7279
7242
  if (written !== bytes.length) {
7280
7243
  throw new Error(`wrote ${written} of ${bytes.length} entry-module name bytes at ${offset}`);
7281
7244
  }
@@ -7372,7 +7335,7 @@ function resignMachOBinary(path) {
7372
7335
  }
7373
7336
 
7374
7337
  // src/bun-compiled-pointer.ts
7375
- import { closeSync as closeSync4, fstatSync as fstatSync2, openSync as openSync4, readSync as readSync3, writeSync as writeSync3 } from "fs";
7338
+ import { closeSync as closeSync4, fstatSync as fstatSync2, openSync as openSync4, readSync as readSync3, writeSync as writeSync2 } from "fs";
7376
7339
  var TWEAKCC_SCAN_STRIDE = 16384n;
7377
7340
  var SCAN_CHUNK = 1 << 20;
7378
7341
  var ELF_MAGIC = 1179403647;
@@ -7540,7 +7503,7 @@ function shimBunCompiledPointer(path) {
7540
7503
  const standInOffset = Number(segment.offset + (standInVaddr - segment.vaddr));
7541
7504
  const displaced = readAt2(fd, 8, standInOffset);
7542
7505
  if (displaced.length !== 8) throw new Error("could not read the bytes Bun's blob pointer displaces");
7543
- writeSync3(fd, needle, 0, 8, standInOffset);
7506
+ writeSync2(fd, needle, 0, 8, standInOffset);
7544
7507
  return { pointerVaddr, standInVaddr, displaced, bunVaddr: bun.addr };
7545
7508
  } finally {
7546
7509
  closeSync4(fd);
@@ -7575,8 +7538,8 @@ function restoreBunCompiledPointer(path, shim) {
7575
7538
  }
7576
7539
  const value = Buffer.alloc(8);
7577
7540
  value.writeBigUInt64LE(written);
7578
- writeSync3(fd, value, 0, 8, pointerOffset);
7579
- writeSync3(fd, shim.displaced, 0, 8, standInOffset);
7541
+ writeSync2(fd, value, 0, 8, pointerOffset);
7542
+ writeSync2(fd, shim.displaced, 0, 8, standInOffset);
7580
7543
  if (!readAt2(fd, 8, pointerOffset).equals(value)) {
7581
7544
  throw new Error("Bun's blob pointer did not take the repacked address");
7582
7545
  }
@@ -7589,7 +7552,7 @@ function restoreBunCompiledPointer(path, shim) {
7589
7552
  }
7590
7553
 
7591
7554
  // src/bun-bundle.ts
7592
- import { closeSync as closeSync5, openSync as openSync5, readSync as readSync4, writeSync as writeSync4 } from "fs";
7555
+ import { closeSync as closeSync5, openSync as openSync5, readSync as readSync4, writeSync as writeSync3 } from "fs";
7593
7556
  function writableModuleIndex(path) {
7594
7557
  const table = readBunModuleTable(path);
7595
7558
  if (!table) return null;
@@ -7804,322 +7767,17 @@ function applyBundleWritePlan(path, plan) {
7804
7767
  function writeAll(fd, bytes, position, path) {
7805
7768
  let written = 0;
7806
7769
  while (written < bytes.length) {
7807
- const wrote = writeSync4(fd, bytes, written, bytes.length - written, position + written);
7770
+ const wrote = writeSync3(fd, bytes, written, bytes.length - written, position + written);
7808
7771
  if (wrote <= 0) throw new Error(`wrote ${written} of ${bytes.length} blob bytes to ${path}`);
7809
7772
  written += wrote;
7810
7773
  }
7811
7774
  }
7812
7775
 
7813
- // src/patch-backup.ts
7814
- import { createHash as createHash5 } from "crypto";
7815
- import { existsSync as existsSync4, readFileSync as readFileSync9, readdirSync, renameSync as renameSync2, rmSync, statSync as statSync7, writeFileSync as writeFileSync5 } from "fs";
7816
- import { homedir as homedir2 } from "os";
7817
- import { join as join7 } from "path";
7818
- var BACKUP_SHA_PREFIX_LENGTH = 16;
7819
- function backupDir() {
7820
- return process.env["TWEAKCC_CONFIG_DIR"]?.trim() || join7(homedir2(), ".tweakcc");
7821
- }
7822
- function sha256File(path) {
7823
- return createHash5("sha256").update(readFileSync9(path)).digest("hex");
7824
- }
7825
- function backupVersionTag(version) {
7826
- const tag = version.trim().replace(/[^\w.-]+/g, "_");
7827
- if (!tag) throw new Error("clodex patch: refusing to name a pristine backup for an empty claude version");
7828
- return tag;
7829
- }
7830
- function contentAddressedBackupPath(version, sha256, dir = backupDir()) {
7831
- return join7(dir, `claude-${backupVersionTag(version)}-${sha256.slice(0, BACKUP_SHA_PREFIX_LENGTH)}.orig`);
7832
- }
7833
- function legacyBackupPath(version, dir = backupDir()) {
7834
- return join7(dir, `claude-${backupVersionTag(version)}.orig`);
7835
- }
7836
- function tweakccMirrorBackupPath(dir = backupDir()) {
7837
- return join7(dir, "native-binary.backup");
7838
- }
7839
- function installProvenancePath(backupPath, binaryPath) {
7840
- const tag = createHash5("sha256").update(binaryPath).digest("hex").slice(0, BACKUP_SHA_PREFIX_LENGTH);
7841
- return `${backupPath}.for-${tag}.json`;
7842
- }
7843
- function installProvenancePattern(name) {
7844
- return new RegExp(`^${name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\.for-[0-9a-f]{${BACKUP_SHA_PREFIX_LENGTH}}\\.json$`);
7845
- }
7846
- function readInstallProvenance(path) {
7847
- let raw;
7848
- try {
7849
- raw = readFileSync9(path, "utf8");
7850
- } catch {
7851
- return null;
7852
- }
7853
- let parsed;
7854
- try {
7855
- parsed = JSON.parse(raw);
7856
- } catch {
7857
- return "damaged";
7858
- }
7859
- if (typeof parsed !== "object" || parsed === null) return "damaged";
7860
- const { install, assumed } = parsed;
7861
- if (typeof install !== "string" || install === "") return "damaged";
7862
- if (typeof assumed !== "boolean") return "damaged";
7863
- return { install, assumed };
7864
- }
7865
- function recordBackupProvenance(backupPath, binaryPath, opts) {
7866
- const target = installProvenancePath(backupPath, binaryPath);
7867
- const existing = readInstallProvenance(target);
7868
- if (existing && existing !== "damaged" && existing.install === binaryPath) {
7869
- if (existing.assumed === opts.assumed) return existing.assumed ? "assumed" : "established";
7870
- if (!existing.assumed) return "established";
7871
- }
7872
- const record = { install: binaryPath, assumed: opts.assumed };
7873
- const temp = `${target}.tmp-${process.pid}-${Date.now().toString(36)}`;
7874
- try {
7875
- writeFileSync5(temp, `${JSON.stringify(record, null, 2)}
7876
- `);
7877
- renameSync2(temp, target);
7878
- return opts.assumed ? "assumed" : "established";
7879
- } catch (err) {
7880
- try {
7881
- rmSync(temp, { force: true });
7882
- } catch {
7883
- }
7884
- throw err;
7885
- }
7886
- }
7887
- function scanPristineBackups(version, dir = backupDir()) {
7888
- const tag = backupVersionTag(version);
7889
- const pattern = new RegExp(`^claude-${tag.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}(?:-([0-9a-f]{${BACKUP_SHA_PREFIX_LENGTH}}))?\\.orig$`);
7890
- let entries;
7891
- try {
7892
- entries = readdirSync(dir);
7893
- } catch {
7894
- return { valid: [], corrupt: [] };
7895
- }
7896
- const provenanceOf = (name) => {
7897
- const recordPattern = installProvenancePattern(name);
7898
- const installs = [];
7899
- const assumedInstalls = [];
7900
- const damagedProvenance = [];
7901
- for (const entry of entries) {
7902
- if (!recordPattern.test(entry)) continue;
7903
- const path = join7(dir, entry);
7904
- const record = readInstallProvenance(path);
7905
- if (record === null || record === "damaged" || installProvenancePath(join7(dir, name), record.install) !== path) {
7906
- damagedProvenance.push(path);
7907
- continue;
7908
- }
7909
- (record.assumed ? assumedInstalls : installs).push(record.install);
7910
- }
7911
- return {
7912
- installs: installs.sort(),
7913
- assumedInstalls: assumedInstalls.sort(),
7914
- damagedProvenance: damagedProvenance.sort()
7915
- };
7916
- };
7917
- const valid = [];
7918
- const corrupt = [];
7919
- for (const entry of entries.sort()) {
7920
- const match = pattern.exec(entry);
7921
- if (!match) continue;
7922
- const path = join7(dir, entry);
7923
- let sha256;
7924
- try {
7925
- if (!statSync7(path).isFile()) continue;
7926
- sha256 = sha256File(path);
7927
- } catch {
7928
- corrupt.push(path);
7929
- continue;
7930
- }
7931
- const embedded = match[1];
7932
- if (embedded) {
7933
- if (sha256.slice(0, BACKUP_SHA_PREFIX_LENGTH) !== embedded) {
7934
- corrupt.push(path);
7935
- continue;
7936
- }
7937
- valid.push({ path, kind: "content-addressed", sha256, ...provenanceOf(entry) });
7938
- } else {
7939
- valid.push({ path, kind: "legacy", sha256, ...provenanceOf(entry) });
7940
- }
7941
- }
7942
- return { valid, corrupt };
7943
- }
7944
- var CLODEX_PATCH_COMMENT_PREFIX = "/*ccpatch:";
7945
- var LEGACY_CLODEX_PATCH_MARKERS = [
7946
- "Additional custom models: ",
7947
- // PATCH 4 — Agent tool model description
7948
- "function(_i){return _i.value===_o.value}",
7949
- // PATCH 5 — picker dedupe guard
7950
- '"clodex:'
7951
- // PATCH 1/3 — canonical ids as model identities
7952
- ];
7953
- function isPatchedClaudeSource(source) {
7954
- return source.includes(CLODEX_PATCH_COMMENT_PREFIX);
7955
- }
7956
- function looksLikeLegacyClodexPatch(source) {
7957
- return !isPatchedClaudeSource(source) && LEGACY_CLODEX_PATCH_MARKERS.some((marker) => source.includes(marker));
7958
- }
7959
- function noBackupMessage(facts) {
7960
- const corrupt = facts.corruptBackups?.length ? ` (${facts.corruptBackups.length} backup file(s) for this version failed integrity checks and were ignored)` : "";
7961
- return `clodex holds no pristine backup of claude ${facts.version} it can attribute to ${facts.binaryPath}: none was found in ${backupDir()}${corrupt}. If that binary is patched, reinstall Claude Code to get a pristine one and run \`clodex patch\` again. If it was never patched, there is nothing to restore.`;
7962
- }
7963
- function identifiesABackup(manifest) {
7964
- return manifest.pristineSha256 !== void 0 || manifest.backupPath !== void 0;
7965
- }
7966
- function recordedBackupGoneMessage(facts, manifest) {
7967
- const named = manifest.backupPath ?? `the backup holding sha256 ${manifest.pristineSha256}`;
7968
- const corrupt = facts.corruptBackups?.length ? ` (${facts.corruptBackups.length} backup file(s) for this version failed integrity checks and were ignored)` : "";
7969
- 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. ` : "";
7970
- 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\`.`;
7971
- }
7972
- function otherInstallMessage(facts, otherBinaryPath) {
7973
- 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.`;
7974
- }
7975
- function otherInstallProvenanceMessage(facts, recorded) {
7976
- const owners = [...new Set(recorded.flatMap((backup) => backup.installs))];
7977
- return `The pristine backup(s) of claude ${facts.version} in ${backupDir()} are recorded as the pristine content of ${owners.join(", ")}, not of ${facts.binaryPath}. Two installs of one Claude Code version are different files, so restoring one of those would overwrite this install with bytes that were never its own. Set TWEAKCC_CC_INSTALLATION_PATH=${owners[0]} to restore that install instead, or reinstall Claude Code to make this one pristine.`;
7978
- }
7979
- function damagedProvenanceMessage(facts, damaged) {
7980
- return `A pristine backup of claude ${facts.version} in ${backupDir()} carries a provenance record that clodex cannot read (${damaged.join(", ")}). Something recorded which install those bytes belong to and that record is damaged, so clodex will not fall back to matching on the claude version in the file name \u2014 on a machine with more than one install that would hand ${facts.binaryPath} bytes that were never its own. Delete the unreadable record to accept that fallback, or reinstall Claude Code to make this install pristine.`;
7981
- }
7982
- function contradictoryProvenanceMessage(facts, mine) {
7983
- return `More than one pristine backup of claude ${facts.version} is recorded as the pristine content of ${facts.binaryPath} (${mine.map((backup) => backup.path).join(", ")}), and they do not hold the same bytes, so clodex cannot tell which one that install holds now \u2014 being snapshotted twice, at two different sets of unpatched bytes, reaches this state without either record being wrong. Delete whichever is stale to decide it: ${mine.map((backup) => installProvenancePath(backup.path, facts.binaryPath)).join(", ")}. Or reinstall the Claude Code at ${facts.binaryPath} and delete both.`;
7984
- }
7985
- function manifestContradictsRecordMessage(facts, manifestChoice, recorded) {
7986
- return `The patch manifest names ${manifestChoice} as the pristine content of ${facts.binaryPath}, but ${recorded.map((backup) => backup.path).join(", ")} is recorded as that install's pristine content and holds different bytes. Nothing establishes which of them the install at that path is now \u2014 an executable replaced in place with a different build of the same claude version reaches exactly this state \u2014 so clodex will not pick one. Delete whichever of these records is stale to decide it: ${recorded.map((backup) => installProvenancePath(backup.path, facts.binaryPath)).join(", ")}. Or reinstall the Claude Code at ${facts.binaryPath} and delete both: a pristine install needs no backup, and \`clodex patch\` will record its own.`;
7987
- }
7988
- function assumedElsewhereMessage(facts, backups) {
7989
- const others = [...new Set(backups.flatMap((backup) => backup.assumedInstalls))];
7990
- return `The pristine backup(s) of claude ${facts.version} in ${backupDir()} have already been restored onto ${others.join(", ")} \u2014 not onto ${facts.binaryPath}. That was itself matched on the claude version in a file name, so it is not proof of ownership, but it is a reason not to hand the same bytes to a second install: two installs of one version are different files. Set TWEAKCC_CC_INSTALLATION_PATH=${others[0]} to restore that install, or reinstall Claude Code to make this one pristine.`;
7991
- }
7992
- function selectRestoreSource(facts) {
7993
- const notes = [];
7994
- let assumedForThisInstall = false;
7995
- const recorded = facts.manifest;
7996
- const speaksForThisVersion = !recorded?.claudeVersion || recorded.claudeVersion === facts.version;
7997
- const manifest = recorded && recorded.binaryPath === facts.binaryPath && speaksForThisVersion ? recorded : null;
7998
- const other = recorded && recorded.binaryPath !== facts.binaryPath && speaksForThisVersion ? recorded : null;
7999
- let chosen = manifest?.pristineSha256 ? facts.backups.find((backup) => backup.sha256 === manifest.pristineSha256) : void 0;
8000
- if (!chosen && manifest?.backupPath) {
8001
- chosen = facts.backups.find((backup) => backup.path === manifest.backupPath);
8002
- }
8003
- if (!chosen && manifest && identifiesABackup(manifest)) {
8004
- return { action: "error", message: recordedBackupGoneMessage(facts, manifest) };
8005
- }
8006
- const manifestDescribesLiveBytes = !!manifest?.patchedSha256 && manifest.patchedSha256 === facts.liveSha256;
8007
- if (chosen && !manifestDescribesLiveBytes) {
8008
- const manifestChoice = chosen;
8009
- const contradicting = facts.backups.filter(
8010
- (backup) => backup.installs.includes(facts.binaryPath) && backup.sha256 !== manifestChoice.sha256
8011
- );
8012
- if (contradicting.length) {
8013
- return {
8014
- action: "error",
8015
- message: manifestContradictsRecordMessage(facts, manifestChoice.path, contradicting)
8016
- };
8017
- }
8018
- }
8019
- if (!chosen) {
8020
- const mine = facts.backups.filter((backup) => backup.installs.includes(facts.binaryPath));
8021
- if (mine.length) {
8022
- if (new Set(mine.map((backup) => backup.sha256)).size > 1) {
8023
- return { action: "error", message: contradictoryProvenanceMessage(facts, mine) };
8024
- }
8025
- chosen = mine.find((backup) => backup.kind === "content-addressed") ?? mine[0];
8026
- }
8027
- }
8028
- if (!chosen) {
8029
- if (other) {
8030
- return facts.backups.length ? { action: "error", message: otherInstallMessage(facts, other.binaryPath) } : { action: "error", message: noBackupMessage(facts) };
8031
- }
8032
- const recordedElsewhere = facts.backups.filter((backup) => backup.installs.length > 0);
8033
- if (recordedElsewhere.length) {
8034
- return { action: "error", message: otherInstallProvenanceMessage(facts, recordedElsewhere) };
8035
- }
8036
- const damaged = facts.backups.flatMap((backup) => backup.damagedProvenance);
8037
- if (damaged.length) {
8038
- return { action: "error", message: damagedProvenanceMessage(facts, damaged) };
8039
- }
8040
- const assumedElsewhere = facts.backups.filter(
8041
- (backup) => backup.assumedInstalls.some((install) => install !== facts.binaryPath)
8042
- );
8043
- if (assumedElsewhere.length) {
8044
- return { action: "error", message: assumedElsewhereMessage(facts, assumedElsewhere) };
8045
- }
8046
- const distinct = [...new Set(facts.backups.map((backup) => backup.sha256))];
8047
- if (distinct.length > 1) {
8048
- return {
8049
- action: "error",
8050
- 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.`
8051
- };
8052
- }
8053
- chosen = facts.backups.find((backup) => backup.kind === "content-addressed") ?? facts.backups[0];
8054
- if (chosen) {
8055
- assumedForThisInstall = true;
8056
- notes.push(
8057
- `Neither a patch manifest nor a provenance record ties ${chosen.path} to ${facts.binaryPath}, so it is being used as that install's pristine content on the strength of its claude ${facts.version} version tag alone \u2014 it predates the records clodex now writes. On a machine with more than one Claude Code install those bytes may belong to the other one, and clodex will not record this guess as provenance.`
8058
- );
8059
- }
8060
- }
8061
- if (!chosen) return { action: "error", message: noBackupMessage(facts) };
8062
- if (manifest?.pristineProvenance === "assumed") assumedForThisInstall = true;
8063
- return {
8064
- action: "restore",
8065
- backupPath: chosen.path,
8066
- pristineSha256: chosen.sha256,
8067
- // A legacy name carries no hash, so its bytes could be anything — including
8068
- // another version's binary, stored under a mislabeled name by an older
8069
- // clodex. Executing it is the only evidence available; require it.
8070
- probeVersion: chosen.kind === "legacy",
8071
- assumedForThisInstall,
8072
- notes
8073
- };
8074
- }
8075
- function planPristineSource(facts) {
8076
- const identical = facts.backups.find((backup) => backup.sha256 === facts.liveSha256 && backup.kind === "content-addressed") ?? facts.backups.find((backup) => backup.sha256 === facts.liveSha256);
8077
- if (identical) {
8078
- return { action: "reuse", backupPath: identical.path, pristineSha256: identical.sha256, notes: [] };
8079
- }
8080
- const manifest = facts.manifest;
8081
- if (manifest && manifest.binaryPath === facts.binaryPath && manifest.patchedSha256 === facts.liveSha256) {
8082
- return selectRestoreSource(facts);
8083
- }
8084
- return { action: "inspect" };
8085
- }
8086
- function planInspectedPristineSource(facts, inspection) {
8087
- if (inspection.patched) return selectRestoreSource(facts);
8088
- const notes = [];
8089
- const conflicting = facts.backups.filter((backup) => backup.sha256 !== facts.liveSha256);
8090
- if (conflicting.length) {
8091
- notes.push(
8092
- `Existing backup(s) for claude ${facts.version} hold different bytes (${conflicting.map((b) => b.path).join(", ")}); the binary being patched carries no clodex patch marker, so it is being stored under its own content address. Both files are kept.`
8093
- );
8094
- }
8095
- return {
8096
- action: "snapshot",
8097
- backupPath: contentAddressedBackupPath(facts.version, facts.liveSha256),
8098
- pristineSha256: facts.liveSha256,
8099
- notes
8100
- };
8101
- }
8102
- function planRestoreOnly(facts) {
8103
- return selectRestoreSource(facts);
8104
- }
8105
- function collectPristineFacts(args) {
8106
- const dir = args.dir ?? backupDir();
8107
- const scan = scanPristineBackups(args.version, dir);
8108
- return {
8109
- version: args.version,
8110
- binaryPath: args.binaryPath,
8111
- liveSha256: existsSync4(args.binaryPath) ? sha256File(args.binaryPath) : "",
8112
- manifest: args.manifest,
8113
- backups: scan.valid,
8114
- corruptBackups: scan.corrupt
8115
- };
8116
- }
8117
-
8118
7776
  // src/provider-factory.ts
8119
7777
  import { wrapLanguageModel, extractReasoningMiddleware } from "ai";
8120
7778
 
8121
7779
  // src/oauth/responses-websocket.ts
8122
- import { createHash as createHash6 } from "crypto";
7780
+ import { createHash as createHash5 } from "crypto";
8123
7781
  import { closeSync as closeSync6, openSync as openSync6 } from "fs";
8124
7782
  import { devNull } from "os";
8125
7783
  import { AsyncLocalStorage } from "async_hooks";
@@ -8183,17 +7841,17 @@ function proxyUrlTargetsListener(proxyUrl, listenerHost, listenerPort, localAddr
8183
7841
  return isWildcard(boundHost) && (isWildcard(proxyHost) || localAddresses.has(proxyHost));
8184
7842
  }
8185
7843
  var dispatcherInstalled = false;
8186
- async function installOutboundProxyDispatcher() {
7844
+ async function installOutboundDispatcher() {
8187
7845
  if (dispatcherInstalled) return true;
8188
- if (!hasOutboundProxyEnv()) return false;
8189
7846
  try {
8190
- const { EnvHttpProxyAgent, setGlobalDispatcher } = await import("undici");
8191
- setGlobalDispatcher(new EnvHttpProxyAgent());
7847
+ const { Agent: Agent3, EnvHttpProxyAgent, setGlobalDispatcher } = await import("undici");
7848
+ const dispatcher = hasOutboundProxyEnv() ? new EnvHttpProxyAgent({ allowH2: false }) : new Agent3({ allowH2: false });
7849
+ setGlobalDispatcher(dispatcher);
8192
7850
  dispatcherInstalled = true;
8193
7851
  return true;
8194
7852
  } catch (err) {
8195
7853
  console.error(
8196
- `clodex: HTTP(S)_PROXY is set but installing the outbound proxy dispatcher failed; using direct connections (${err instanceof Error ? err.message : String(err)})`
7854
+ `clodex: installing the outbound fetch dispatcher failed; continuing with Node's existing dispatcher (${err instanceof Error ? err.message : String(err)})`
8197
7855
  );
8198
7856
  return false;
8199
7857
  }
@@ -8487,6 +8145,69 @@ function sanitizeToolInput(input, requiredProps) {
8487
8145
  return out;
8488
8146
  }
8489
8147
 
8148
+ // src/tool-input-coerce.ts
8149
+ var SCALAR_KINDS = /* @__PURE__ */ new Set(["number", "integer", "boolean"]);
8150
+ var LOCAL_REF = /^#\/(?:\$defs|definitions)\/([^/]+)$/;
8151
+ var MAX_SCHEMA_VISITS = 64;
8152
+ function preferredName(kinds) {
8153
+ let compound;
8154
+ let scalar;
8155
+ let hasString = false;
8156
+ for (const kind of kinds) {
8157
+ if (kind === "array" || kind === "object") compound ??= kind;
8158
+ else if (kind === "string") hasString = true;
8159
+ else if (kind !== void 0 && kind !== "null") scalar ??= kind;
8160
+ }
8161
+ return compound ?? (hasString ? "string" : scalar);
8162
+ }
8163
+ function schemaScalarKind(schema, parameters) {
8164
+ const defs = parameters && typeof parameters === "object" ? parameters.$defs ?? parameters.definitions : void 0;
8165
+ const visited = /* @__PURE__ */ new Set();
8166
+ const resolve4 = (node) => {
8167
+ if (visited.size > MAX_SCHEMA_VISITS || visited.has(node) || !node || typeof node !== "object") return void 0;
8168
+ visited.add(node);
8169
+ const record = node;
8170
+ if (typeof record.type === "string") return record.type;
8171
+ if (Array.isArray(record.type)) {
8172
+ const named = preferredName(record.type.filter((entry) => typeof entry === "string"));
8173
+ if (named !== void 0) return named;
8174
+ }
8175
+ if (typeof record.$ref === "string" && defs && typeof defs === "object") {
8176
+ const match = LOCAL_REF.exec(record.$ref);
8177
+ if (match) return resolve4(defs[match[1]]);
8178
+ }
8179
+ for (const branches of [record.anyOf, record.oneOf]) {
8180
+ if (!Array.isArray(branches)) continue;
8181
+ const named = preferredName(branches.map(resolve4));
8182
+ if (named !== void 0) return named;
8183
+ }
8184
+ return void 0;
8185
+ };
8186
+ if (!schema || typeof schema !== "object" || Array.isArray(schema)) return void 0;
8187
+ const kind = resolve4(schema);
8188
+ return kind !== void 0 && SCALAR_KINDS.has(kind) ? kind : void 0;
8189
+ }
8190
+ var DECIMAL_LITERAL = /^[-+]?\d+(\.\d+)?$/;
8191
+ var BOOLEAN_LITERAL = /^\uFEFF?[ \t\n\r]*(true|false)[ \t\n\r]*$/;
8192
+ var MAX_SIGNIFICANT_DIGITS = 15;
8193
+ function significantDigits(decimal) {
8194
+ const [integer = "", fraction = ""] = decimal.replace(/^[-+]/, "").split(".");
8195
+ return integer.replace(/^0+/, "").length + fraction.length;
8196
+ }
8197
+ function coerceEchoedScalar(value, kind) {
8198
+ if (typeof value !== "string") return value;
8199
+ if (kind === "boolean") {
8200
+ const match = BOOLEAN_LITERAL.exec(value);
8201
+ return match ? match[1] === "true" : value;
8202
+ }
8203
+ const trimmed = value.trim();
8204
+ if (DECIMAL_LITERAL.test(trimmed)) {
8205
+ return significantDigits(trimmed) <= MAX_SIGNIFICANT_DIGITS ? Number(trimmed) : value;
8206
+ }
8207
+ const numeric = Number(value);
8208
+ return Number.isFinite(numeric) && String(numeric) === value ? numeric : value;
8209
+ }
8210
+
8490
8211
  // src/upstream-retry.ts
8491
8212
  var UPSTREAM_MAX_RETRIES_ENV = "CLODEX_UPSTREAM_MAX_RETRIES";
8492
8213
  var UPSTREAM_IDLE_TIMEOUT_ENV = "CLODEX_UPSTREAM_IDLE_TIMEOUT_MS";
@@ -8958,7 +8679,7 @@ function hasResponsesLiteHeader(headers) {
8958
8679
  }
8959
8680
  function authorizationHeaderFingerprint(headers) {
8960
8681
  const authorization = Object.entries(headers).find(([key]) => key.toLowerCase() === "authorization")?.[1];
8961
- return authorization ? createHash6("sha256").update(authorization).digest("hex") : "";
8682
+ return authorization ? createHash5("sha256").update(authorization).digest("hex") : "";
8962
8683
  }
8963
8684
  function bodyToString(body) {
8964
8685
  if (body == null) return "";
@@ -8991,13 +8712,13 @@ function responsesWebSocketPromptFingerprint(payload) {
8991
8712
  delete stable.previous_response_id;
8992
8713
  delete stable.stream;
8993
8714
  delete stable.background;
8994
- return createHash6("sha256").update(canonicalJson(stable)).digest("hex");
8715
+ return createHash5("sha256").update(canonicalJson(stable)).digest("hex");
8995
8716
  }
8996
8717
  function responsesWebSocketPromptFieldHashes(payload) {
8997
8718
  const hashes = {};
8998
8719
  for (const key of Object.keys(payload).sort()) {
8999
8720
  if (key === "input" || key === "previous_response_id" || key === "stream" || key === "background") continue;
9000
- hashes[key] = createHash6("sha256").update(canonicalJson(payload[key])).digest("hex").slice(0, 12);
8721
+ hashes[key] = createHash5("sha256").update(canonicalJson(payload[key])).digest("hex").slice(0, 12);
9001
8722
  }
9002
8723
  return hashes;
9003
8724
  }
@@ -9034,7 +8755,7 @@ function responsesWebSocketPartitionKey(wsUrl, payload, options = {}, authorizat
9034
8755
  authorizationFingerprint,
9035
8756
  claudeAgentId
9036
8757
  ].join("");
9037
- return createHash6("sha256").update(material).digest("hex");
8758
+ return createHash5("sha256").update(material).digest("hex");
9038
8759
  }
9039
8760
  function inputArray(payload) {
9040
8761
  return Array.isArray(payload.input) ? payload.input : [];
@@ -9054,9 +8775,12 @@ function toolSchemaDefaults(payload) {
9054
8775
  if (!properties || typeof properties !== "object") return;
9055
8776
  const perTool = /* @__PURE__ */ new Map();
9056
8777
  for (const [prop, schema] of Object.entries(properties)) {
9057
- if (schema && typeof schema === "object" && "default" in schema) {
9058
- perTool.set(prop, canonicalJson(schema.default));
9059
- }
8778
+ if (!schema || typeof schema !== "object") continue;
8779
+ const rule = {};
8780
+ if ("default" in schema) rule.default = canonicalJson(schema.default);
8781
+ const scalar = schemaScalarKind(schema, parameters);
8782
+ if (scalar) rule.scalar = scalar;
8783
+ if (rule.default !== void 0 || rule.scalar) perTool.set(prop, rule);
9060
8784
  }
9061
8785
  if (perTool.size) defaults.set(record.name, perTool);
9062
8786
  };
@@ -9069,7 +8793,7 @@ function toolSchemaDefaultsFingerprint(defaults) {
9069
8793
  name,
9070
8794
  [...perTool.entries()].sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0)
9071
8795
  ]);
9072
- return createHash6("sha256").update(JSON.stringify(tuples)).digest("hex").slice(0, 16);
8796
+ return createHash5("sha256").update(JSON.stringify(tuples)).digest("hex").slice(0, 16);
9073
8797
  }
9074
8798
  function stripSchemaDefaults(name, args, defaults) {
9075
8799
  if (!defaults) return args;
@@ -9078,9 +8802,10 @@ function stripSchemaDefaults(name, args, defaults) {
9078
8802
  if (!perTool) return args;
9079
8803
  const out = {};
9080
8804
  for (const [key, value] of Object.entries(args)) {
9081
- const expected = perTool.get(key);
9082
- if (expected !== void 0 && canonicalJson(value) === expected) continue;
9083
- out[key] = value;
8805
+ const rule = perTool.get(key);
8806
+ const typed = rule?.scalar ? coerceEchoedScalar(value, rule.scalar) : value;
8807
+ if (rule?.default !== void 0 && canonicalJson(typed) === rule.default) continue;
8808
+ out[key] = typed;
9084
8809
  }
9085
8810
  return out;
9086
8811
  }
@@ -9118,7 +8843,7 @@ function conversationItemKind(value) {
9118
8843
  return "object";
9119
8844
  }
9120
8845
  function conversationItemHash(value, defaults) {
9121
- return createHash6("sha256").update(canonicalJson(normalizeToolCallJson(value, defaults))).digest("hex").slice(0, 16);
8846
+ return createHash5("sha256").update(canonicalJson(normalizeToolCallJson(value, defaults))).digest("hex").slice(0, 16);
9122
8847
  }
9123
8848
  function reasoningNormalizationGap(expected, actual, defaults) {
9124
8849
  if (conversationItemKind(expected) !== "reasoning" || conversationItemKind(actual) !== "reasoning") return void 0;
@@ -9418,7 +9143,7 @@ function diagnosticTextFingerprint(field, value) {
9418
9143
  if (typeof value !== "string" || value.length === 0) return {};
9419
9144
  return {
9420
9145
  [`${field}Bytes`]: Buffer.byteLength(value),
9421
- [`${field}Hash`]: createHash6("sha256").update(value).digest("hex").slice(0, 16)
9146
+ [`${field}Hash`]: createHash5("sha256").update(value).digest("hex").slice(0, 16)
9422
9147
  };
9423
9148
  }
9424
9149
  function responseFailureDetails(event) {
@@ -9454,7 +9179,7 @@ function emitResponseErrorDiagnostic(entry, ctx, details) {
9454
9179
  emitContextDiagnostic(entry, ctx, { event: "ws_response_error", ...details });
9455
9180
  }
9456
9181
  function diagnosticItemIdHash(value) {
9457
- return typeof value === "string" && value.length > 0 ? createHash6("sha256").update(value).digest("hex").slice(0, 16) : void 0;
9182
+ return typeof value === "string" && value.length > 0 ? createHash5("sha256").update(value).digest("hex").slice(0, 16) : void 0;
9458
9183
  }
9459
9184
  function reasoningPartIndex(value) {
9460
9185
  return typeof value === "number" && Number.isInteger(value) && value >= 0 ? value : void 0;
@@ -9969,7 +9694,10 @@ function transportReplaySafe(ctx) {
9969
9694
  }
9970
9695
  function handleSocketMessage(entry, data) {
9971
9696
  const ctx = entry.current;
9972
- if (!ctx || ctx.closed) return;
9697
+ if (!ctx || ctx.closed) {
9698
+ if (entry.connectionDiagnostic) observeIdleFrame(entry, data);
9699
+ return;
9700
+ }
9973
9701
  const text5 = Array.isArray(data) ? Buffer.concat(data).toString("utf8") : data.toString("utf8");
9974
9702
  ctx.frameCount += 1;
9975
9703
  if (ctx.transportRetryPending) {
@@ -9989,6 +9717,7 @@ function handleSocketMessage(entry, data) {
9989
9717
  return;
9990
9718
  }
9991
9719
  const type = eventType(event);
9720
+ if (isQuotaEvent(type)) observeQuotaEvent(entry, event, "during_response", ctx.emitDiagnostic);
9992
9721
  trackReasoningProtocol(entry, ctx, event, type);
9993
9722
  captureOutput(ctx, event);
9994
9723
  if (type === "response.completed") {
@@ -10180,7 +9909,62 @@ function numericRetryAfterHeader(value) {
10180
9909
  const single = Array.isArray(value) ? value[0] : value;
10181
9910
  return typeof single === "string" && /^\d+$/.test(single.trim()) ? Number(single.trim()) : void 0;
10182
9911
  }
10183
- function createConnection(WebSocket, wsUrl, headers, persistent, key, options, debug, agent) {
9912
+ function isQuotaEvent(type) {
9913
+ return type === "codex.rate_limits";
9914
+ }
9915
+ var QUOTA_LEDGER_MAX_BYTES = 8e3;
9916
+ var QUOTA_FIELDS_MAX_COUNT = 24;
9917
+ function boundedLedger(value) {
9918
+ let serialized;
9919
+ try {
9920
+ serialized = JSON.stringify(value);
9921
+ } catch {
9922
+ return {};
9923
+ }
9924
+ if (serialized === void 0) return {};
9925
+ const bytes = Buffer.byteLength(serialized);
9926
+ return bytes <= QUOTA_LEDGER_MAX_BYTES ? { value, bytes } : { bytes };
9927
+ }
9928
+ function observeQuotaEvent(entry, event, phase, emit) {
9929
+ if (!emit) return;
9930
+ const record = event;
9931
+ const rate = boundedLedger(record.rate_limits);
9932
+ const additional = boundedLedger(record.additional_rate_limits);
9933
+ const codeReview = boundedLedger(record.code_review_rate_limits);
9934
+ const credits = boundedLedger(record.credits);
9935
+ const promo = boundedLedger(record.promo);
9936
+ emit({
9937
+ event: "ws_rate_limits",
9938
+ connectionId: entry.debugId,
9939
+ generation: entry.generation,
9940
+ phase,
9941
+ upstreamEventType: "codex.rate_limits",
9942
+ fieldCount: Object.keys(record).length,
9943
+ fieldsPresent: Object.keys(record).slice(0, QUOTA_FIELDS_MAX_COUNT).map(boundedDiagnosticIdentifier).filter((name) => name !== void 0),
9944
+ rateLimits: rate.value,
9945
+ rateLimitsBytes: rate.bytes,
9946
+ additionalRateLimits: additional.value,
9947
+ additionalRateLimitsBytes: additional.bytes,
9948
+ codeReviewRateLimits: codeReview.value,
9949
+ codeReviewRateLimitsBytes: codeReview.bytes,
9950
+ credits: credits.value,
9951
+ creditsBytes: credits.bytes,
9952
+ promo: promo.value,
9953
+ promoBytes: promo.bytes,
9954
+ planType: boundedDiagnosticIdentifier(record.plan_type)
9955
+ });
9956
+ }
9957
+ function observeIdleFrame(entry, data) {
9958
+ let event;
9959
+ try {
9960
+ event = JSON.parse(Array.isArray(data) ? Buffer.concat(data).toString("utf8") : data.toString("utf8"));
9961
+ } catch {
9962
+ return;
9963
+ }
9964
+ if (!isQuotaEvent(eventType(event))) return;
9965
+ observeQuotaEvent(entry, event, "idle", entry.connectionDiagnostic);
9966
+ }
9967
+ function createConnection(WebSocket, wsUrl, headers, persistent, key, options, debug, agent, connectionDiagnostic) {
10184
9968
  const now = options.now();
10185
9969
  const socket = new WebSocket(wsUrl, agent ? { headers, agent } : { headers });
10186
9970
  const entry = {
@@ -10195,7 +9979,8 @@ function createConnection(WebSocket, wsUrl, headers, persistent, key, options, d
10195
9979
  lastUsedAt: now,
10196
9980
  inFlight: false,
10197
9981
  options,
10198
- debug
9982
+ debug,
9983
+ connectionDiagnostic
10199
9984
  };
10200
9985
  if (persistent && key) registerEntry(entry);
10201
9986
  debug(
@@ -10560,7 +10345,7 @@ function createResponsesWebSocketFetch(wsUrl, log12, options = {}) {
10560
10345
  keyTuple: {
10561
10346
  wsUrl,
10562
10347
  providerId: options.providerId ?? "openai",
10563
- accountIdHash: options.accountId ? createHash6("sha256").update(options.accountId).digest("hex").slice(0, 16) : "",
10348
+ accountIdHash: options.accountId ? createHash5("sha256").update(options.accountId).digest("hex").slice(0, 16) : "",
10564
10349
  model: typeof payload.model === "string" ? payload.model : void 0,
10565
10350
  effort: typeof payload.reasoning?.effort === "string" ? String(payload.reasoning.effort).trim().toLowerCase() : "",
10566
10351
  promptCacheKey: typeof payload.prompt_cache_key === "string" ? payload.prompt_cache_key : void 0,
@@ -10612,6 +10397,7 @@ function createResponsesWebSocketFetch(wsUrl, log12, options = {}) {
10612
10397
  })),
10613
10398
  evictions
10614
10399
  }, diagnosticCorrelation);
10400
+ const connectionDiagnostic = options.onDiagnostic ? (event) => emitDiagnostic(options, event, {}) : void 0;
10615
10401
  let activeContext;
10616
10402
  const stream = new ReadableStream({
10617
10403
  start(controller) {
@@ -10644,7 +10430,8 @@ function createResponsesWebSocketFetch(wsUrl, log12, options = {}) {
10644
10430
  partitionKey,
10645
10431
  resolvedOptions,
10646
10432
  debug,
10647
- proxyAgent
10433
+ proxyAgent,
10434
+ connectionDiagnostic
10648
10435
  )
10649
10436
  };
10650
10437
  activeContext = ctx;
@@ -10656,7 +10443,8 @@ function createResponsesWebSocketFetch(wsUrl, log12, options = {}) {
10656
10443
  partitionKey,
10657
10444
  resolvedOptions,
10658
10445
  debug,
10659
- proxyAgent
10446
+ proxyAgent,
10447
+ connectionDiagnostic
10660
10448
  );
10661
10449
  dispatchContext(entry, ctx);
10662
10450
  const signal = init?.signal;
@@ -10688,7 +10476,7 @@ function createResponsesWebSocketFetch(wsUrl, log12, options = {}) {
10688
10476
  }
10689
10477
 
10690
10478
  // src/oauth/claude-identity.ts
10691
- import { createHash as createHash7, randomUUID as randomUUID3 } from "crypto";
10479
+ import { createHash as createHash6, randomUUID as randomUUID3 } from "crypto";
10692
10480
  var CLAUDE_CODE_CLI_VERSION = "2.1.195";
10693
10481
  var CLAUDE_CODE_USER_AGENT = `claude-cli/${CLAUDE_CODE_CLI_VERSION} (external, cli)`;
10694
10482
  var CLAUDE_CODE_ENTRYPOINT = process.env.CLAUDE_CODE_ENTRYPOINT ?? "cli";
@@ -10703,7 +10491,7 @@ function getOrCreateSessionId(seed) {
10703
10491
  return id;
10704
10492
  }
10705
10493
  function uuidFromHash(input) {
10706
- const h = createHash7("sha256").update(input).digest("hex");
10494
+ const h = createHash6("sha256").update(input).digest("hex");
10707
10495
  return [
10708
10496
  h.slice(0, 8),
10709
10497
  h.slice(8, 12),
@@ -10717,7 +10505,7 @@ var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
10717
10505
  function resolveCliUserID(providerData, seed) {
10718
10506
  const v = providerData?.cliUserID;
10719
10507
  if (typeof v === "string" && HEX64_RE.test(v)) return v;
10720
- return createHash7("sha256").update(`cliUserID:${seed}`).digest("hex");
10508
+ return createHash6("sha256").update(`cliUserID:${seed}`).digest("hex");
10721
10509
  }
10722
10510
  function resolveAccountUUID(providerData, seed) {
10723
10511
  const v = providerData?.accountUUID;
@@ -11621,7 +11409,7 @@ function thinkingProviderOptions(npm) {
11621
11409
 
11622
11410
  // src/proxy.ts
11623
11411
  import { createServer } from "http";
11624
- import { appendFileSync as appendFileSync2, openSync as openSync7, writeSync as writeSync5, closeSync as closeSync7 } from "fs";
11412
+ import { appendFileSync as appendFileSync2, openSync as openSync7, writeSync as writeSync4, closeSync as closeSync7 } from "fs";
11625
11413
 
11626
11414
  // src/http-utils.ts
11627
11415
  import * as zlib from "zlib";
@@ -11904,7 +11692,13 @@ function anthropicUpstreamHeaders(apiKey, stream = false, inboundBeta, authType,
11904
11692
  }
11905
11693
  var UpstreamUnreachableError = class extends Error {
11906
11694
  constructor(cause) {
11907
- super(`Upstream unreachable: ${cause instanceof Error ? cause.message : String(cause)}`);
11695
+ const detail = cause instanceof Error ? cause.message : String(cause);
11696
+ const directCode = cause !== null && typeof cause === "object" ? cause.code : void 0;
11697
+ const nestedCause = cause instanceof Error ? cause.cause : void 0;
11698
+ const nestedCode = nestedCause !== null && typeof nestedCause === "object" ? nestedCause.code : void 0;
11699
+ const code = typeof directCode === "string" ? directCode : typeof nestedCode === "string" ? nestedCode : void 0;
11700
+ const detailWithCode = code && !detail.includes(code) ? detail ? `${detail} (${code})` : code : detail;
11701
+ super(`Upstream unreachable: ${detailWithCode}`, { cause });
11908
11702
  this.name = "UpstreamUnreachableError";
11909
11703
  }
11910
11704
  };
@@ -12048,7 +11842,7 @@ async function relayAnthropicMessages(res, messagesUrl, body, apiKey, clientWant
12048
11842
  import { randomUUID as randomUUID4 } from "crypto";
12049
11843
 
12050
11844
  // src/sdk-adapter.ts
12051
- import { createHash as createHash8 } from "crypto";
11845
+ import { createHash as createHash7 } from "crypto";
12052
11846
  import { streamText, generateText, tool, jsonSchema } from "ai";
12053
11847
 
12054
11848
  // src/proxy-shared.ts
@@ -12478,7 +12272,7 @@ function extractClaudeAgentIds(headers) {
12478
12272
  };
12479
12273
  }
12480
12274
  function claudeSessionPromptCacheKey(sessionId) {
12481
- return "relay-session-" + createHash8("sha256").update(sessionId).digest("hex").slice(0, 32);
12275
+ return "relay-session-" + createHash7("sha256").update(sessionId).digest("hex").slice(0, 32);
12482
12276
  }
12483
12277
  function anthropicEffortFromRequest(body) {
12484
12278
  const effort = body.output_config?.effort;
@@ -12488,7 +12282,7 @@ function anthropicEffortFromRequest(body) {
12488
12282
  function openAiPromptCacheKey(system, tools) {
12489
12283
  const toolSig = (tools ?? []).map((t) => `${t.name}${t.description ?? ""}${JSON.stringify(t.input_schema ?? {})}`).join("");
12490
12284
  const material = `${system ?? ""}\0${toolSig}`;
12491
- return "relay-" + createHash8("sha256").update(material).digest("hex").slice(0, 32);
12285
+ return "relay-" + createHash7("sha256").update(material).digest("hex").slice(0, 32);
12492
12286
  }
12493
12287
  function supportsOpenAiPromptCacheBreakpoints(modelId) {
12494
12288
  const match = modelId.toLowerCase().match(/^gpt-(\d+)(?:\.(\d+))?(?:-|$)/);
@@ -13421,7 +13215,7 @@ function appendSecureLog(logPath, line) {
13421
13215
  try {
13422
13216
  const fd = openSync7(logPath, "a", 384);
13423
13217
  try {
13424
- writeSync5(fd, `${(/* @__PURE__ */ new Date()).toISOString()} ${redacted}
13218
+ writeSync4(fd, `${(/* @__PURE__ */ new Date()).toISOString()} ${redacted}
13425
13219
  `);
13426
13220
  } finally {
13427
13221
  closeSync7(fd);
@@ -14121,25 +13915,12 @@ function buildHttpProxyRoutes(providers, favorites, modelAliases = void 0, max =
14121
13915
  }
14122
13916
 
14123
13917
  // src/patcher.ts
14124
- function getPatchManifestPath() {
14125
- return join8(getAppHome(), "patch-state.json");
14126
- }
14127
13918
  function getPatchLockPath() {
14128
- return join8(getAppHome(), "patch.lock");
14129
- }
14130
- function readPatchManifest(path = getPatchManifestPath()) {
14131
- try {
14132
- const parsed = JSON.parse(readFileSync10(path, "utf8"));
14133
- if (parsed && typeof parsed.binaryPath === "string" && typeof parsed.configHash === "string") {
14134
- return parsed;
14135
- }
14136
- } catch {
14137
- }
14138
- return null;
13919
+ return join7(getAppHome(), "patch.lock");
14139
13920
  }
14140
13921
  function writePatchManifest(manifest, path = getPatchManifestPath()) {
14141
13922
  mkdirSync5(getAppHome(), { recursive: true, mode: 448 });
14142
- writeFileSync6(path, `${JSON.stringify(manifest, null, 2)}
13923
+ writeFileSync5(path, `${JSON.stringify(manifest, null, 2)}
14143
13924
  `, { encoding: "utf8", mode: 384 });
14144
13925
  }
14145
13926
  function buildPatchModelConfig(favorites, aliases, modelMetaFor) {
@@ -14214,7 +13995,7 @@ function computePatchConfigHash(config, transformsVersion = PATCH_TRANSFORMS_VER
14214
13995
  if (localPatchIdentity !== void 0) {
14215
13996
  payload.push(["local-patches", localPatchIdentity]);
14216
13997
  }
14217
- return createHash9("sha256").update(JSON.stringify(payload)).digest("hex");
13998
+ return createHash8("sha256").update(JSON.stringify(payload)).digest("hex");
14218
13999
  }
14219
14000
  function buildDesiredPatchConfig(options = {}) {
14220
14001
  const prefs = loadPreferences();
@@ -14292,12 +14073,12 @@ function pidIsAlive(pid) {
14292
14073
  function tryAcquirePatchLock(lockPath = getPatchLockPath(), opts = {}) {
14293
14074
  const now = opts.now ?? Date.now();
14294
14075
  const isAlive = opts.isAlive ?? pidIsAlive;
14295
- mkdirSync5(join8(lockPath, ".."), { recursive: true, mode: 448 });
14076
+ mkdirSync5(join7(lockPath, ".."), { recursive: true, mode: 448 });
14296
14077
  for (let attempt = 0; attempt < 2; attempt++) {
14297
14078
  try {
14298
14079
  const fd = openSync8(lockPath, "wx");
14299
14080
  const content = { pid: process.pid, startedAt: now };
14300
- writeFileSync6(fd, JSON.stringify(content));
14081
+ writeFileSync5(fd, JSON.stringify(content));
14301
14082
  closeSync8(fd);
14302
14083
  return () => {
14303
14084
  try {
@@ -14308,7 +14089,7 @@ function tryAcquirePatchLock(lockPath = getPatchLockPath(), opts = {}) {
14308
14089
  } catch {
14309
14090
  let stale = false;
14310
14091
  try {
14311
- const existing = JSON.parse(readFileSync10(lockPath, "utf8"));
14092
+ const existing = JSON.parse(readFileSync9(lockPath, "utf8"));
14312
14093
  stale = !existing.pid || !isAlive(existing.pid) || typeof existing.startedAt === "number" && now - existing.startedAt > PATCH_LOCK_STALE_MS;
14313
14094
  } catch {
14314
14095
  stale = true;
@@ -14330,11 +14111,11 @@ function takeIgnoredLaunchOverride() {
14330
14111
  }
14331
14112
  function resolveClaudeBinaryForPatch() {
14332
14113
  const envOverride = process.env["TWEAKCC_CC_INSTALLATION_PATH"]?.trim() || null;
14333
- const nativeSymlink = join8(homedir3(), ".local", "bin", "claude");
14334
- if (envOverride && !existsSync5(envOverride)) {
14114
+ const nativeSymlink = join7(homedir2(), ".local", "bin", "claude");
14115
+ if (envOverride && !existsSync4(envOverride)) {
14335
14116
  return { ok: false, reason: "patch-target-missing", declaredPath: envOverride };
14336
14117
  }
14337
- const source = envOverride || (existsSync5(nativeSymlink) ? nativeSymlink : null) || findClaudeBinary();
14118
+ const source = envOverride || (existsSync4(nativeSymlink) ? nativeSymlink : null) || findClaudeBinary();
14338
14119
  if (!source) return { ok: false, reason: "binary-not-found" };
14339
14120
  let resolved;
14340
14121
  try {
@@ -14366,7 +14147,7 @@ function resolveClaudeBinaryForPatch() {
14366
14147
  }
14367
14148
  ignoredLaunchOverride = launchOverrideResolved && launchOverrideResolved !== resolved ? { used: resolved, ignored: launchOverride } : null;
14368
14149
  try {
14369
- if (!statSync8(resolved).isFile()) return { ok: false, reason: "binary-not-found" };
14150
+ if (!statSync7(resolved).isFile()) return { ok: false, reason: "binary-not-found" };
14370
14151
  } catch {
14371
14152
  return { ok: false, reason: "binary-not-found" };
14372
14153
  }
@@ -14427,10 +14208,10 @@ function publishFileByRename(from, to, mode) {
14427
14208
  try {
14428
14209
  copyFileSync(from, temp);
14429
14210
  if (mode !== void 0) chmodSync4(temp, mode);
14430
- renameSync3(temp, to);
14211
+ renameSync2(temp, to);
14431
14212
  } catch (err) {
14432
14213
  try {
14433
- rmSync2(temp, { force: true });
14214
+ rmSync(temp, { force: true });
14434
14215
  } catch {
14435
14216
  }
14436
14217
  throw err;
@@ -14498,8 +14279,8 @@ async function applyPatch(binaryPath, version, desired, configHash, opts) {
14498
14279
  try {
14499
14280
  mkdirSync5(backupDir(), { recursive: true });
14500
14281
  const { tryDetectInstallation, readContent, writeContent } = await import("tweakcc");
14501
- candidateDir = mkdtempSync(join8(dirname5(binaryPath), ".clodex-patch-"));
14502
- const candidatePath = join8(candidateDir, basename(binaryPath));
14282
+ candidateDir = mkdtempSync(join7(dirname5(binaryPath), ".clodex-patch-"));
14283
+ const candidatePath = join7(candidateDir, basename(binaryPath));
14503
14284
  const seedCandidate = async (from) => {
14504
14285
  copyFileSync(from, candidatePath);
14505
14286
  const shim = shimEntryModuleName(candidatePath);
@@ -14646,9 +14427,9 @@ async function applyPatch(binaryPath, version, desired, configHash, opts) {
14646
14427
  }
14647
14428
  if (writeShim) restoreEntryModuleName(candidatePath, writeShim, { resign: true });
14648
14429
  else if (publishedBlob) resignMachOBinary(candidatePath);
14649
- patchedSize = statSync8(candidatePath).size;
14430
+ patchedSize = statSync7(candidatePath).size;
14650
14431
  patchedSha256 = sha256File(candidatePath);
14651
- renameSync3(candidatePath, binaryPath);
14432
+ renameSync2(candidatePath, binaryPath);
14652
14433
  } catch (err) {
14653
14434
  const detailLines = err instanceof PatchApplyError ? summarizePatchResults(err.results) : [];
14654
14435
  if (opts.trace && detailLines.length) {
@@ -14663,7 +14444,7 @@ async function applyPatch(binaryPath, version, desired, configHash, opts) {
14663
14444
  } finally {
14664
14445
  if (candidateDir !== void 0) {
14665
14446
  try {
14666
- rmSync2(candidateDir, { recursive: true, force: true });
14447
+ rmSync(candidateDir, { recursive: true, force: true });
14667
14448
  } catch (err) {
14668
14449
  if (opts.trace) {
14669
14450
  process.stderr.write(
@@ -14737,7 +14518,7 @@ function runRestoreCommand(target) {
14737
14518
  }
14738
14519
  if (manifest && manifest.backupPath && manifest.binaryPath === binaryPath && manifest.claudeVersion !== version && manifest.backupPath !== plan.backupPath) {
14739
14520
  try {
14740
- if (existsSync5(manifest.backupPath)) {
14521
+ if (existsSync4(manifest.backupPath)) {
14741
14522
  recordBackupProvenance(manifest.backupPath, manifest.binaryPath, {
14742
14523
  assumed: manifest.pristineProvenance === "assumed"
14743
14524
  });
@@ -14766,7 +14547,7 @@ function runRestoreCommand(target) {
14766
14547
  }
14767
14548
  let publishedMode;
14768
14549
  try {
14769
- publishedMode = statSync8(binaryPath).mode;
14550
+ publishedMode = statSync7(binaryPath).mode;
14770
14551
  } catch {
14771
14552
  }
14772
14553
  try {
@@ -14825,7 +14606,7 @@ async function runPatchCommand(opts = {}) {
14825
14606
  binaryPath,
14826
14607
  claudeVersion: version,
14827
14608
  configHash,
14828
- binarySize: statSync8(binaryPath).size
14609
+ binarySize: statSync7(binaryPath).size
14829
14610
  });
14830
14611
  if (state === "current") {
14831
14612
  p2.log.success(`claude ${version} is already patched with the current model config \u2014 nothing to do.`);
@@ -14838,7 +14619,7 @@ async function runPatchCommand(opts = {}) {
14838
14619
  }
14839
14620
  if (manifest && manifest.backupPath && (manifest.binaryPath !== binaryPath || manifest.claudeVersion !== version)) {
14840
14621
  try {
14841
- if (existsSync5(manifest.backupPath)) {
14622
+ if (existsSync4(manifest.backupPath)) {
14842
14623
  recordBackupProvenance(manifest.backupPath, manifest.binaryPath, {
14843
14624
  assumed: manifest.pristineProvenance === "assumed"
14844
14625
  });
@@ -14894,7 +14675,7 @@ async function runLaunchPatchCheck(opts = {}) {
14894
14675
  binaryPath: resolved.binaryPath,
14895
14676
  claudeVersion: resolved.version,
14896
14677
  configHash,
14897
- binarySize: statSync8(resolved.binaryPath).size
14678
+ binarySize: statSync7(resolved.binaryPath).size
14898
14679
  });
14899
14680
  if (state === "current") return;
14900
14681
  const interactive = !opts.dryRun && !opts.agentStdout && process.stdin.isTTY === true && process.stdout.isTTY === true;
@@ -15231,16 +15012,16 @@ function localProvidersToServerModels(localProviders) {
15231
15012
  import { randomUUID as randomUUID5 } from "crypto";
15232
15013
  import {
15233
15014
  closeSync as closeSync9,
15234
- existsSync as existsSync6,
15015
+ existsSync as existsSync5,
15235
15016
  fstatSync as fstatSync3,
15236
15017
  fsyncSync as fsyncSync2,
15237
15018
  lstatSync,
15238
15019
  mkdirSync as mkdirSync6,
15239
15020
  openSync as openSync9,
15240
- readFileSync as readFileSync11,
15241
- renameSync as renameSync4,
15021
+ readFileSync as readFileSync10,
15022
+ renameSync as renameSync3,
15242
15023
  unlinkSync as unlinkSync4,
15243
- writeFileSync as writeFileSync7
15024
+ writeFileSync as writeFileSync6
15244
15025
  } from "fs";
15245
15026
  import { dirname as dirname6 } from "path";
15246
15027
  var JOURNAL_SCHEMA_VERSION = 1;
@@ -15321,7 +15102,7 @@ function parseJournal(raw) {
15321
15102
  };
15322
15103
  }
15323
15104
  function readJournalUnlocked(path) {
15324
- if (!existsSync6(path)) return emptyJournal();
15105
+ if (!existsSync5(path)) return emptyJournal();
15325
15106
  let fd;
15326
15107
  try {
15327
15108
  const before = lstatSync(path);
@@ -15344,7 +15125,7 @@ function readJournalUnlocked(path) {
15344
15125
  if (opened.size > MAX_JOURNAL_BYTES) {
15345
15126
  throw new Error("Credential cleanup journal is too large.");
15346
15127
  }
15347
- return parseJournal(JSON.parse(readFileSync11(fd, "utf8")));
15128
+ return parseJournal(JSON.parse(readFileSync10(fd, "utf8")));
15348
15129
  } catch (error) {
15349
15130
  const message = error instanceof Error ? error.message : String(error);
15350
15131
  throw new Error(`Could not read credential cleanup journal: ${message}`);
@@ -15372,13 +15153,13 @@ function writeJournalUnlocked(journal, path) {
15372
15153
  let fd;
15373
15154
  try {
15374
15155
  fd = openSync9(tmp, "wx", FILE_MODE4);
15375
- writeFileSync7(fd, `${JSON.stringify(journal, null, 2)}
15156
+ writeFileSync6(fd, `${JSON.stringify(journal, null, 2)}
15376
15157
  `);
15377
15158
  fsyncSync2(fd);
15378
15159
  closeSync9(fd);
15379
15160
  fd = void 0;
15380
15161
  assertRegistryWriteOwnership(path);
15381
- renameSync4(tmp, path);
15162
+ renameSync3(tmp, path);
15382
15163
  syncParentDirectory(path);
15383
15164
  } finally {
15384
15165
  if (fd !== void 0) closeSync9(fd);
@@ -19357,8 +19138,8 @@ import { createBrotliDecompress, createGunzip, createInflate } from "zlib";
19357
19138
 
19358
19139
  // src/http-proxy/ca.ts
19359
19140
  import { randomBytes as randomBytes2 } from "crypto";
19360
- import { chmodSync as chmodSync5, existsSync as existsSync7, mkdirSync as mkdirSync7, readFileSync as readFileSync12, writeFileSync as writeFileSync8 } from "fs";
19361
- import { dirname as dirname7, join as join9, resolve as resolve3 } from "path";
19141
+ import { chmodSync as chmodSync5, existsSync as existsSync6, mkdirSync as mkdirSync7, readFileSync as readFileSync11, writeFileSync as writeFileSync7 } from "fs";
19142
+ import { dirname as dirname7, join as join8, resolve as resolve3 } from "path";
19362
19143
  import forge from "node-forge";
19363
19144
  var CERT_DIR = "http-proxy";
19364
19145
  var CA_CERT_FILE = "clodex-ca.pem";
@@ -19373,22 +19154,22 @@ function serialNumber() {
19373
19154
  return bytes.toString("hex");
19374
19155
  }
19375
19156
  function certPaths() {
19376
- const dir = join9(getAppHome(), CERT_DIR);
19157
+ const dir = join8(getAppHome(), CERT_DIR);
19377
19158
  return {
19378
19159
  dir,
19379
- caCert: join9(dir, CA_CERT_FILE),
19380
- caKey: join9(dir, CA_KEY_FILE),
19381
- serverCert: join9(dir, SERVER_CERT_FILE),
19382
- serverKey: join9(dir, SERVER_KEY_FILE),
19383
- version: join9(dir, CERT_VERSION_FILE)
19160
+ caCert: join8(dir, CA_CERT_FILE),
19161
+ caKey: join8(dir, CA_KEY_FILE),
19162
+ serverCert: join8(dir, SERVER_CERT_FILE),
19163
+ serverKey: join8(dir, SERVER_KEY_FILE),
19164
+ version: join8(dir, CERT_VERSION_FILE)
19384
19165
  };
19385
19166
  }
19386
19167
  function writePrivate(path, value) {
19387
- writeFileSync8(path, value, { encoding: "utf8", mode: 384 });
19168
+ writeFileSync7(path, value, { encoding: "utf8", mode: 384 });
19388
19169
  chmodSync5(path, 384);
19389
19170
  }
19390
19171
  function writePublic(path, value) {
19391
- writeFileSync8(path, value, { encoding: "utf8", mode: 420 });
19172
+ writeFileSync7(path, value, { encoding: "utf8", mode: 420 });
19392
19173
  chmodSync5(path, 420);
19393
19174
  }
19394
19175
  function generateCertificates(paths) {
@@ -19433,8 +19214,8 @@ function generateCertificates(paths) {
19433
19214
  }
19434
19215
  function storedCertificatesAreCurrent(paths) {
19435
19216
  try {
19436
- const ca = forge.pki.certificateFromPem(readFileSync12(paths.caCert, "utf8"));
19437
- const server = forge.pki.certificateFromPem(readFileSync12(paths.serverCert, "utf8"));
19217
+ const ca = forge.pki.certificateFromPem(readFileSync11(paths.caCert, "utf8"));
19218
+ const server = forge.pki.certificateFromPem(readFileSync11(paths.serverCert, "utf8"));
19438
19219
  const now = Date.now();
19439
19220
  const renewalBuffer = 7 * 24 * 60 * 60 * 1e3;
19440
19221
  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);
@@ -19445,13 +19226,13 @@ function storedCertificatesAreCurrent(paths) {
19445
19226
  function ensureHttpProxyCertificates() {
19446
19227
  const paths = certPaths();
19447
19228
  const required = [paths.caCert, paths.caKey, paths.serverCert, paths.serverKey, paths.version];
19448
- const current = required.every(existsSync7) && readFileSync12(paths.version, "utf8") === CERT_VERSION && storedCertificatesAreCurrent(paths);
19229
+ const current = required.every(existsSync6) && readFileSync11(paths.version, "utf8") === CERT_VERSION && storedCertificatesAreCurrent(paths);
19449
19230
  if (!current) generateCertificates(paths);
19450
19231
  return {
19451
19232
  caCertPath: paths.caCert,
19452
- caCert: readFileSync12(paths.caCert, "utf8"),
19453
- serverCert: readFileSync12(paths.serverCert, "utf8"),
19454
- serverKey: readFileSync12(paths.serverKey, "utf8")
19233
+ caCert: readFileSync11(paths.caCert, "utf8"),
19234
+ serverCert: readFileSync11(paths.serverCert, "utf8"),
19235
+ serverKey: readFileSync11(paths.serverKey, "utf8")
19455
19236
  };
19456
19237
  }
19457
19238
  function ensureHttpProxyCaBundle(relayCaCertPath, additionalCaCertPath, onWarning) {
@@ -19465,7 +19246,7 @@ function ensureHttpProxyCaBundle(relayCaCertPath, additionalCaCertPath, onWarnin
19465
19246
  let additionalCa;
19466
19247
  try {
19467
19248
  if (resolve3(additionalCaCertPath) === resolve3(relayCaCertPath)) return relayCaCertPath;
19468
- additionalCa = readFileSync12(additionalCaCertPath, "utf8").trim();
19249
+ additionalCa = readFileSync11(additionalCaCertPath, "utf8").trim();
19469
19250
  } catch (err) {
19470
19251
  return warn(
19471
19252
  `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.`
@@ -19475,8 +19256,8 @@ function ensureHttpProxyCaBundle(relayCaCertPath, additionalCaCertPath, onWarnin
19475
19256
  return warn(`NODE_EXTRA_CA_CERTS=${additionalCaCertPath} is empty, so it adds nothing.`);
19476
19257
  }
19477
19258
  try {
19478
- const relayCa = readFileSync12(relayCaCertPath, "utf8").trimEnd();
19479
- const combinedPath = join9(dirname7(relayCaCertPath), "combined-ca.pem");
19259
+ const relayCa = readFileSync11(relayCaCertPath, "utf8").trimEnd();
19260
+ const combinedPath = join8(dirname7(relayCaCertPath), "combined-ca.pem");
19480
19261
  writePublic(combinedPath, `${relayCa}
19481
19262
  ${additionalCa}
19482
19263
  `);
@@ -20371,6 +20152,7 @@ async function startHttpProxy(options) {
20371
20152
  mitmServer.emit("connection", clientSocket);
20372
20153
  return;
20373
20154
  }
20155
+ clientSocket.once("error", () => clientSocket.destroy());
20374
20156
  const target = authorityParts(req.url ?? "");
20375
20157
  if (!target) {
20376
20158
  clientSocket.end("HTTP/1.1 400 Bad Request\r\n\r\n");
@@ -20615,6 +20397,7 @@ async function runHttpProxyServerCommand(debug = false, webSocketDiagnostics = f
20615
20397
  if (handle.webSocketDiagnosticsLogPath) {
20616
20398
  console.log(` WebSocket diagnostics: ${handle.webSocketDiagnosticsLogPath}`);
20617
20399
  console.log(pc10.yellow(" Diagnostic mode records request headers and metadata; credential headers are redacted."));
20400
+ console.log(pc10.yellow(" It also records OpenAI usage-limit reports verbatim, including account credits and promotions."));
20618
20401
  }
20619
20402
  console.log("");
20620
20403
  printHttpProxyModels(loaded.routes, loaded.aliases);
@@ -20994,6 +20777,7 @@ async function runServerCommand(options = {}) {
20994
20777
  if (webSocketDiagnosticsLogPath) {
20995
20778
  console.log(` WebSocket diagnostics: ${webSocketDiagnosticsLogPath}`);
20996
20779
  console.log(pc11.yellow(" Diagnostic mode records request headers and metadata; credential headers are redacted."));
20780
+ console.log(pc11.yellow(" It also records OpenAI usage-limit reports verbatim, including account credits and promotions."));
20997
20781
  }
20998
20782
  if (mode === "network") {
20999
20783
  for (const { name, address } of getLocalIps()) {
@@ -21283,6 +21067,227 @@ function planLaunchWizard(opts) {
21283
21067
  return { skip: false, target: null };
21284
21068
  }
21285
21069
 
21070
+ // src/vscode-launcher.ts
21071
+ import { spawnSync as spawnSync2 } from "child_process";
21072
+ import { existsSync as existsSync7, mkdirSync as mkdirSync8, mkdtempSync as mkdtempSync2, readdirSync, readFileSync as readFileSync12, rmSync as rmSync2, statSync as statSync8, writeFileSync as writeFileSync8 } from "fs";
21073
+ import { tmpdir } from "os";
21074
+ import { dirname as dirname8, join as join9 } from "path";
21075
+ import { fileURLToPath } from "url";
21076
+ var LAUNCHER_EXE_NAME = "clodex-claude.exe";
21077
+ var VSCODE_WRAPPER_SETTING = "claudeCode.claudeProcessWrapper";
21078
+ var NODE_PATH_PLACEHOLDER = '"__CLODEX_NODE_PATH__"';
21079
+ var WRAPPER_PATH_PLACEHOLDER = '"__CLODEX_WRAPPER_SCRIPT_PATH__"';
21080
+ var LAUNCHER_SOURCE_FILE = "clodex-claude-launcher.cs";
21081
+ var UTF8_BOM = "\uFEFF";
21082
+ var CSC_ARGS = ["/nologo", "/target:exe", "/platform:anycpu", "/optimize+", `/out:${LAUNCHER_EXE_NAME}`, LAUNCHER_SOURCE_FILE];
21083
+ var packageRoot = join9(dirname8(fileURLToPath(import.meta.url)), "..");
21084
+ function defaultLauncherInputs() {
21085
+ return {
21086
+ launcherSourcePath: join9(packageRoot, "launcher", LAUNCHER_SOURCE_FILE),
21087
+ wrapperScriptPath: join9(packageRoot, "dist", "claude-wrapper.js")
21088
+ };
21089
+ }
21090
+ function launcherInstallPath(env = process.env) {
21091
+ return join9(getAppHome(env), "bin", LAUNCHER_EXE_NAME);
21092
+ }
21093
+ function csharpStringLiteral(value) {
21094
+ let literal = '"';
21095
+ for (let i = 0; i < value.length; i += 1) {
21096
+ const unit = value.charCodeAt(i);
21097
+ if (unit === 92) literal += "\\\\";
21098
+ else if (unit === 34) literal += '\\"';
21099
+ else if (unit >= 32 && unit <= 126) literal += value[i];
21100
+ else literal += `\\u${unit.toString(16).padStart(4, "0")}`;
21101
+ }
21102
+ return `${literal}"`;
21103
+ }
21104
+ function renderLauncherSource(template, paths) {
21105
+ const replaceOnce = (source, placeholder, value) => {
21106
+ const first = source.indexOf(placeholder);
21107
+ if (first === -1 || source.indexOf(placeholder, first + 1) !== -1) {
21108
+ throw new Error(`launcher source must contain ${placeholder} exactly once`);
21109
+ }
21110
+ return source.slice(0, first) + csharpStringLiteral(value) + source.slice(first + placeholder.length);
21111
+ };
21112
+ const withNode = replaceOnce(template, NODE_PATH_PLACEHOLDER, paths.nodePath);
21113
+ return replaceOnce(withNode, WRAPPER_PATH_PLACEHOLDER, paths.wrapperScriptPath);
21114
+ }
21115
+ function cscSearchRoots(env = process.env) {
21116
+ const windir = env.WINDIR?.trim() || env.SystemRoot?.trim() || "C:\\Windows";
21117
+ const dotnet = join9(windir, "Microsoft.NET");
21118
+ return [join9(dotnet, "Framework64"), join9(dotnet, "Framework")];
21119
+ }
21120
+ function frameworkVersionKey(name) {
21121
+ return name.slice(1).split(".").map((part) => Number.parseInt(part, 10) || 0);
21122
+ }
21123
+ function compareVersionKeys(a, b) {
21124
+ for (let i = 0; i < Math.max(a.length, b.length); i += 1) {
21125
+ const diff = (a[i] ?? 0) - (b[i] ?? 0);
21126
+ if (diff !== 0) return diff;
21127
+ }
21128
+ return 0;
21129
+ }
21130
+ function findCsc(env = process.env) {
21131
+ for (const root of cscSearchRoots(env)) {
21132
+ let entries;
21133
+ try {
21134
+ entries = readdirSync(root);
21135
+ } catch {
21136
+ continue;
21137
+ }
21138
+ const candidates = entries.filter((name) => /^v4\.\d+(\.\d+)*$/i.test(name)).sort((a, b) => compareVersionKeys(frameworkVersionKey(b), frameworkVersionKey(a)));
21139
+ for (const version of candidates) {
21140
+ const csc = join9(root, version, "csc.exe");
21141
+ try {
21142
+ if (statSync8(csc).isFile()) return csc;
21143
+ } catch {
21144
+ }
21145
+ }
21146
+ }
21147
+ return null;
21148
+ }
21149
+ function formatWrapperSetting(exePath) {
21150
+ return `"${VSCODE_WRAPPER_SETTING}": ${JSON.stringify(exePath)}`;
21151
+ }
21152
+ var runCsc = (cscPath, args, cwd) => {
21153
+ const result = spawnSync2(cscPath, [...args], {
21154
+ cwd,
21155
+ encoding: "utf8",
21156
+ stdio: ["ignore", "pipe", "pipe"],
21157
+ windowsHide: true
21158
+ });
21159
+ return {
21160
+ status: result.status,
21161
+ stdout: result.stdout ?? "",
21162
+ stderr: result.stderr ?? "",
21163
+ ...result.error ? { error: result.error } : {}
21164
+ };
21165
+ };
21166
+ function describeError(err) {
21167
+ return err instanceof Error ? err.message : String(err);
21168
+ }
21169
+ function installVscodeLauncher(opts = {}) {
21170
+ const platform = opts.platform ?? process.platform;
21171
+ const env = opts.env ?? process.env;
21172
+ const defaults = defaultLauncherInputs();
21173
+ const nodePath = opts.nodePath ?? process.execPath;
21174
+ const wrapperScriptPath = opts.wrapperScriptPath ?? defaults.wrapperScriptPath;
21175
+ const launcherSourcePath = opts.launcherSourcePath ?? defaults.launcherSourcePath;
21176
+ const compile = opts.compile ?? runCsc;
21177
+ if (platform !== "win32") {
21178
+ return {
21179
+ ok: false,
21180
+ reason: "not-windows",
21181
+ message: `clodex install-vscode-launcher is only needed on Windows. On macOS and Linux npm installs clodex-claude as a program the VS Code extension can spawn directly: set ${VSCODE_WRAPPER_SETTING} to the path printed by \`which clodex-claude\`.`
21182
+ };
21183
+ }
21184
+ const cscPath = findCsc(env);
21185
+ if (!cscPath) {
21186
+ return {
21187
+ ok: false,
21188
+ reason: "no-csc",
21189
+ message: `could not find the .NET Framework C# compiler (csc.exe). It ships with Windows 10/11 under ${cscSearchRoots(env).join(" or ")}\\v4.*; if it is missing, install .NET Framework 4.8 from Microsoft and re-run.`
21190
+ };
21191
+ }
21192
+ for (const [label, path] of [["claude-wrapper.js", wrapperScriptPath], ["node.exe", nodePath], ["launcher source", launcherSourcePath]]) {
21193
+ if (!existsSync7(path)) {
21194
+ return { ok: false, reason: "missing-input", message: `${label} is not at ${path}; reinstall clodex and re-run.` };
21195
+ }
21196
+ }
21197
+ let source;
21198
+ try {
21199
+ source = renderLauncherSource(readFileSync12(launcherSourcePath, "utf8"), { nodePath, wrapperScriptPath });
21200
+ } catch (err) {
21201
+ return { ok: false, reason: "missing-input", message: `could not prepare the launcher source: ${describeError(err)}` };
21202
+ }
21203
+ const exePath = launcherInstallPath(env);
21204
+ const tempRoot = opts.tempRoot ?? tmpdir();
21205
+ let scratch;
21206
+ try {
21207
+ scratch = mkdtempSync2(join9(tempRoot, "clodex-vscode-launcher-"));
21208
+ } catch (err) {
21209
+ return { ok: false, reason: "install-failed", message: `could not create a scratch directory under ${tempRoot}: ${describeError(err)}` };
21210
+ }
21211
+ let result;
21212
+ try {
21213
+ result = buildAndPublish({ scratch, source, cscPath, compile, exePath, nodePath, wrapperScriptPath });
21214
+ } catch (err) {
21215
+ result = { ok: false, reason: "install-failed", message: `could not build the launcher: ${describeError(err)}` };
21216
+ }
21217
+ const cleanupWarning = removeScratch(scratch);
21218
+ return cleanupWarning ? { ...result, warning: cleanupWarning } : result;
21219
+ }
21220
+ function buildAndPublish(step) {
21221
+ const { scratch, cscPath, exePath } = step;
21222
+ writeFileSync8(join9(scratch, LAUNCHER_SOURCE_FILE), UTF8_BOM + step.source, "utf8");
21223
+ const compiled = step.compile(cscPath, CSC_ARGS, scratch);
21224
+ const builtExe = join9(scratch, LAUNCHER_EXE_NAME);
21225
+ if (compiled.error || compiled.status !== 0 || !existsSync7(builtExe)) {
21226
+ const output = [compiled.stdout, compiled.stderr].map((text5) => text5.trim()).filter(Boolean).join("\n");
21227
+ return {
21228
+ ok: false,
21229
+ reason: "compile-failed",
21230
+ message: compiled.error ? `could not run ${cscPath}: ${compiled.error.message}` : `${cscPath} failed (exit ${compiled.status ?? "signal"}) while building the launcher.`,
21231
+ ...output ? { detail: output } : {}
21232
+ };
21233
+ }
21234
+ try {
21235
+ mkdirSync8(dirname8(exePath), { recursive: true });
21236
+ publishFileByRename(builtExe, exePath);
21237
+ } catch (err) {
21238
+ return {
21239
+ ok: false,
21240
+ reason: "install-failed",
21241
+ message: `could not write ${exePath}: ${describeError(err)}. If VS Code is running a chat through the launcher, close it and re-run.`
21242
+ };
21243
+ }
21244
+ return { ok: true, exePath, cscPath, nodePath: step.nodePath, wrapperScriptPath: step.wrapperScriptPath };
21245
+ }
21246
+ function removeScratch(scratch) {
21247
+ try {
21248
+ rmSync2(scratch, { recursive: true, force: true, maxRetries: 3, retryDelay: 100 });
21249
+ return void 0;
21250
+ } catch (err) {
21251
+ return `could not remove the scratch directory ${scratch}: ${describeError(err)}`;
21252
+ }
21253
+ }
21254
+ function successReport(result) {
21255
+ return [
21256
+ `Built ${result.exePath}`,
21257
+ ` compiler: ${result.cscPath}`,
21258
+ ` runs: ${result.nodePath} ${result.wrapperScriptPath}`,
21259
+ "",
21260
+ "Add this to your VS Code settings (Ctrl+Shift+P \u2192 Preferences: Open User Settings (JSON)):",
21261
+ "",
21262
+ ` ${formatWrapperSetting(result.exePath)}`,
21263
+ "",
21264
+ "Then reload the window (Developer: Reload Window). Keep `clodex server --proxy` running:",
21265
+ "the launcher starts clodex-claude, which finds that server and bridges each session to it,",
21266
+ "so HTTPS_PROXY/HTTP_PROXY/NODE_EXTRA_CA_CERTS entries in claudeCode.environmentVariables",
21267
+ "are no longer needed (remove them so Claude still starts when the server is down).",
21268
+ "",
21269
+ "With `clodex patch` applied to your installed Claude Code, clodex-claude runs that patched",
21270
+ "install in place of the extension's bundled claude.exe when the two builds match, which is",
21271
+ "what puts clodex models in the extension's model picker. Re-run `clodex patch` after either",
21272
+ "the extension or the CLI updates; a mismatched chat runs the bundled binary and logs one line",
21273
+ `to the extension's "Claude VSCode" output channel.`,
21274
+ "",
21275
+ "Re-run `clodex install-vscode-launcher` after switching Node versions or moving the clodex",
21276
+ "install: the paths above are compiled into the executable."
21277
+ ];
21278
+ }
21279
+ function runInstallVscodeLauncherCommand(opts = {}) {
21280
+ const result = installVscodeLauncher(opts);
21281
+ if (result.warning) console.error(`clodex: warning: ${result.warning}`);
21282
+ if (!result.ok) {
21283
+ console.error(`clodex: ${result.message}`);
21284
+ if (result.detail) console.error(result.detail);
21285
+ return 1;
21286
+ }
21287
+ console.log(successReport(result).join("\n"));
21288
+ return 0;
21289
+ }
21290
+
21286
21291
  // src/cli.ts
21287
21292
  var STARTER_CLAUDE_FLAGS = /* @__PURE__ */ new Set(["--dry-run", "--trace", "--fast", "--endpoint", "--proxy", "--save-mode", "--help", "-h", "--version", "-v"]);
21288
21293
  var CLODEX_LAUNCH_FLAGS = /* @__PURE__ */ new Set(["--provider", "--model", "--context"]);
@@ -21504,6 +21509,15 @@ function parseArgs(args) {
21504
21509
  }
21505
21510
  return parsed2;
21506
21511
  }
21512
+ if (first === "install-vscode-launcher") {
21513
+ const parsed2 = emptyParsed("install-vscode-launcher");
21514
+ for (const arg of rest) {
21515
+ if (arg === "--help" || arg === "-h") parsed2.showHelp = true;
21516
+ else if (arg === "--version" || arg === "-v") parsed2.showVersion = true;
21517
+ else if (!parsed2.error) parsed2.error = `Unknown install-vscode-launcher option: ${arg}`;
21518
+ }
21519
+ return parsed2;
21520
+ }
21507
21521
  if (first !== "claude") {
21508
21522
  return {
21509
21523
  ...emptyParsed("root"),
@@ -21549,6 +21563,7 @@ ${pc13.bold("Usage:")}
21549
21563
  clodex models
21550
21564
  clodex favorites
21551
21565
  clodex providers
21566
+ clodex install-vscode-launcher
21552
21567
  clodex --help
21553
21568
  clodex --version
21554
21569
 
@@ -21563,6 +21578,9 @@ ${pc13.bold("Commands:")}
21563
21578
  models Manage favorite models and aliases (max ${MAX_MODEL_CATALOG})
21564
21579
  favorites Alias for models
21565
21580
  providers Add or sign in to model providers
21581
+ install-vscode-launcher
21582
+ Windows only: build the .exe the Claude Code VS Code extension
21583
+ needs to launch Claude through clodex
21566
21584
 
21567
21585
  ${pc13.bold("Bridge modes (claude and server):")}
21568
21586
  --endpoint Local Anthropic-format gateway; Claude Code launches with
@@ -21669,8 +21687,9 @@ ${pc13.bold("Common options (both modes):")}
21669
21687
  ~/.clodex/server-runtime.json, so the
21670
21688
  clodex-claude wrapper never bridges to it
21671
21689
  (CLODEX_NO_DISCOVERY=1 works too)
21672
- --ws-diagnostics Log sanitized request envelopes and WebSocket
21673
- head decisions
21690
+ --ws-diagnostics Log sanitized request envelopes, WebSocket
21691
+ head decisions, and the server's usage-limit
21692
+ reports verbatim (includes account credits)
21674
21693
  --help, --version Help / version
21675
21694
 
21676
21695
  ${pc13.bold("Endpoint mode only")} ${pc13.dim("(error if combined with --proxy)")}:
@@ -21803,6 +21822,42 @@ ${pc13.bold("Behavior:")}
21803
21822
  permissions. Local failures are reported but never block the built-ins.
21804
21823
  Run clodex patch again after every claude update.`;
21805
21824
  }
21825
+ function installVscodeLauncherHelpText() {
21826
+ return `${pc13.bold("clodex install-vscode-launcher")} v${VERSION}
21827
+ Windows only. Build a small native clodex-claude.exe that the Claude Code VS Code
21828
+ extension can use as its claudeCode.claudeProcessWrapper, so chats in the editor
21829
+ launch Claude Code through clodex.
21830
+
21831
+ ${pc13.bold("Usage:")}
21832
+ clodex install-vscode-launcher
21833
+ clodex install-vscode-launcher --help
21834
+
21835
+ ${pc13.bold("Behavior:")}
21836
+ The extension spawns its process wrapper without a shell, and on Windows npm
21837
+ installs clodex-claude as script shims (clodex-claude, .cmd, .ps1) with no .exe,
21838
+ so the setting fails with spawn EINVAL.
21839
+ This command compiles launcher/clodex-claude-launcher.cs from the clodex package
21840
+ with the C# compiler that ships inside the .NET Framework on Windows 10/11
21841
+ (csc.exe under %WINDIR%\\Microsoft.NET), writes the result to
21842
+ ~/.clodex/bin/clodex-claude.exe, and prints the VS Code setting to paste.
21843
+ Nothing is downloaded and no settings file is edited.
21844
+
21845
+ The launcher runs node.exe with clodex's claude-wrapper.js and hands the
21846
+ extension's arguments through unchanged, so the wrapper bridges the session to
21847
+ a running clodex server --proxy. Node and Claude Code are tied to the launcher
21848
+ with a Windows job object so that cancelling a chat leaves no stray processes;
21849
+ if Windows refuses the job object the launcher says so on stderr and runs anyway.
21850
+ As on macOS and Linux, clodex-claude then runs your clodex-patched install in
21851
+ place of the bundled claude.exe when the two builds match, so clodex models
21852
+ appear in the extension's model picker; re-run clodex patch after updates.
21853
+
21854
+ The paths to node.exe and to the clodex install are compiled in: re-run this
21855
+ command after switching Node versions or moving the clodex install.
21856
+ On macOS and Linux it is not needed \u2014 point the setting at clodex-claude itself.
21857
+
21858
+ ${pc13.bold("Setup guide:")}
21859
+ docs/windows-setup.md in the clodex package (also on GitHub).`;
21860
+ }
21806
21861
  function printHelp(text5) {
21807
21862
  console.log(`
21808
21863
  ${text5}
@@ -22606,7 +22661,7 @@ Error: ${launchPlan.error}
22606
22661
  return exitCode;
22607
22662
  }
22608
22663
  async function main(args = process.argv.slice(2)) {
22609
- await installOutboundProxyDispatcher();
22664
+ await installOutboundDispatcher();
22610
22665
  const parsed = parseArgs(args);
22611
22666
  if (parsed.error) {
22612
22667
  console.error(pc13.red(`
@@ -22712,6 +22767,17 @@ Error: ${resolved.error}
22712
22767
  localPatches: parsed.patchLocalPatches
22713
22768
  });
22714
22769
  }
22770
+ if (parsed.command === "install-vscode-launcher") {
22771
+ if (parsed.showVersion) {
22772
+ console.log(VERSION);
22773
+ return 0;
22774
+ }
22775
+ if (parsed.showHelp) {
22776
+ printHelp(installVscodeLauncherHelpText());
22777
+ return 0;
22778
+ }
22779
+ return runInstallVscodeLauncherCommand();
22780
+ }
22715
22781
  if (parsed.showVersion) {
22716
22782
  console.log(VERSION);
22717
22783
  return 0;
@@ -22734,7 +22800,7 @@ Error: ${resolved.error}
22734
22800
  function isCliEntryPoint() {
22735
22801
  if (!process.argv[1]) return false;
22736
22802
  try {
22737
- return realpathSync3(fileURLToPath(import.meta.url)) === realpathSync3(process.argv[1]);
22803
+ return realpathSync3(fileURLToPath2(import.meta.url)) === realpathSync3(process.argv[1]);
22738
22804
  } catch {
22739
22805
  return false;
22740
22806
  }
@@ -22752,6 +22818,7 @@ if (isCliEntryPoint()) {
22752
22818
  }
22753
22819
  export {
22754
22820
  claudeHelpText,
22821
+ installVscodeLauncherHelpText,
22755
22822
  main,
22756
22823
  modelsHelpText,
22757
22824
  parseArgs,