@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/README.md +6 -2
- package/dist/{chunk-ZM2KYLPH.js → chunk-3NCDMS7N.js} +468 -2
- package/dist/chunk-3NCDMS7N.js.map +1 -0
- package/dist/claude-wrapper.js +276 -7
- package/dist/claude-wrapper.js.map +1 -1
- package/dist/cli.js +555 -488
- package/dist/cli.js.map +1 -1
- package/docs/background-agents.md +35 -0
- package/docs/windows-setup.md +203 -168
- package/launcher/clodex-claude-launcher.cs +311 -0
- package/package.json +2 -1
- package/dist/chunk-ZM2KYLPH.js.map +0 -1
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
|
-
|
|
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.
|
|
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
|
|
3848
|
+
import { createHash as createHash8 } from "crypto";
|
|
3886
3849
|
import {
|
|
3887
3850
|
chmodSync as chmodSync4,
|
|
3888
3851
|
copyFileSync,
|
|
3889
|
-
existsSync as
|
|
3852
|
+
existsSync as existsSync4,
|
|
3890
3853
|
mkdtempSync,
|
|
3891
3854
|
mkdirSync as mkdirSync5,
|
|
3892
|
-
readFileSync as
|
|
3893
|
-
renameSync as
|
|
3894
|
-
rmSync
|
|
3895
|
-
statSync as
|
|
3855
|
+
readFileSync as readFileSync9,
|
|
3856
|
+
renameSync as renameSync2,
|
|
3857
|
+
rmSync,
|
|
3858
|
+
statSync as statSync7,
|
|
3896
3859
|
unlinkSync as unlinkSync3,
|
|
3897
|
-
writeFileSync as
|
|
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
|
|
3903
|
-
import { basename, dirname as dirname5, join as
|
|
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
|
|
7056
|
-
const packageJsonPath = join6(
|
|
7057
|
-
const installScriptPath = join6(
|
|
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
|
|
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 =
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
7579
|
-
|
|
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
|
|
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 =
|
|
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
|
|
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
|
|
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
|
-
|
|
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:
|
|
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 ?
|
|
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
|
|
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] =
|
|
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
|
|
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
|
|
9058
|
-
|
|
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
|
|
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
|
|
9082
|
-
|
|
9083
|
-
|
|
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
|
|
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`]:
|
|
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 ?
|
|
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)
|
|
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
|
|
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 ?
|
|
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
|
|
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 =
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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-" +
|
|
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-" +
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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(
|
|
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
|
-
|
|
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(
|
|
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 =
|
|
14334
|
-
if (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 || (
|
|
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 (!
|
|
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
|
-
|
|
14211
|
+
renameSync2(temp, to);
|
|
14431
14212
|
} catch (err) {
|
|
14432
14213
|
try {
|
|
14433
|
-
|
|
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(
|
|
14502
|
-
const candidatePath =
|
|
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 =
|
|
14430
|
+
patchedSize = statSync7(candidatePath).size;
|
|
14650
14431
|
patchedSha256 = sha256File(candidatePath);
|
|
14651
|
-
|
|
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
|
-
|
|
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 (
|
|
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 =
|
|
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:
|
|
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 (
|
|
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:
|
|
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
|
|
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
|
|
15241
|
-
renameSync as
|
|
15021
|
+
readFileSync as readFileSync10,
|
|
15022
|
+
renameSync as renameSync3,
|
|
15242
15023
|
unlinkSync as unlinkSync4,
|
|
15243
|
-
writeFileSync as
|
|
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 (!
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
19361
|
-
import { dirname as dirname7, join as
|
|
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 =
|
|
19157
|
+
const dir = join8(getAppHome(), CERT_DIR);
|
|
19377
19158
|
return {
|
|
19378
19159
|
dir,
|
|
19379
|
-
caCert:
|
|
19380
|
-
caKey:
|
|
19381
|
-
serverCert:
|
|
19382
|
-
serverKey:
|
|
19383
|
-
version:
|
|
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
|
-
|
|
19168
|
+
writeFileSync7(path, value, { encoding: "utf8", mode: 384 });
|
|
19388
19169
|
chmodSync5(path, 384);
|
|
19389
19170
|
}
|
|
19390
19171
|
function writePublic(path, value) {
|
|
19391
|
-
|
|
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(
|
|
19437
|
-
const server = forge.pki.certificateFromPem(
|
|
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(
|
|
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:
|
|
19453
|
-
serverCert:
|
|
19454
|
-
serverKey:
|
|
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 =
|
|
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 =
|
|
19479
|
-
const combinedPath =
|
|
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
|
|
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
|
|
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(
|
|
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,
|