@bman654/clodex 2.13.1 → 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 +541 -481
- 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";
|
|
@@ -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";
|
|
@@ -12054,7 +11842,7 @@ async function relayAnthropicMessages(res, messagesUrl, body, apiKey, clientWant
|
|
|
12054
11842
|
import { randomUUID as randomUUID4 } from "crypto";
|
|
12055
11843
|
|
|
12056
11844
|
// src/sdk-adapter.ts
|
|
12057
|
-
import { createHash as
|
|
11845
|
+
import { createHash as createHash7 } from "crypto";
|
|
12058
11846
|
import { streamText, generateText, tool, jsonSchema } from "ai";
|
|
12059
11847
|
|
|
12060
11848
|
// src/proxy-shared.ts
|
|
@@ -12484,7 +12272,7 @@ function extractClaudeAgentIds(headers) {
|
|
|
12484
12272
|
};
|
|
12485
12273
|
}
|
|
12486
12274
|
function claudeSessionPromptCacheKey(sessionId) {
|
|
12487
|
-
return "relay-session-" +
|
|
12275
|
+
return "relay-session-" + createHash7("sha256").update(sessionId).digest("hex").slice(0, 32);
|
|
12488
12276
|
}
|
|
12489
12277
|
function anthropicEffortFromRequest(body) {
|
|
12490
12278
|
const effort = body.output_config?.effort;
|
|
@@ -12494,7 +12282,7 @@ function anthropicEffortFromRequest(body) {
|
|
|
12494
12282
|
function openAiPromptCacheKey(system, tools) {
|
|
12495
12283
|
const toolSig = (tools ?? []).map((t) => `${t.name}${t.description ?? ""}${JSON.stringify(t.input_schema ?? {})}`).join("");
|
|
12496
12284
|
const material = `${system ?? ""}\0${toolSig}`;
|
|
12497
|
-
return "relay-" +
|
|
12285
|
+
return "relay-" + createHash7("sha256").update(material).digest("hex").slice(0, 32);
|
|
12498
12286
|
}
|
|
12499
12287
|
function supportsOpenAiPromptCacheBreakpoints(modelId) {
|
|
12500
12288
|
const match = modelId.toLowerCase().match(/^gpt-(\d+)(?:\.(\d+))?(?:-|$)/);
|
|
@@ -13427,7 +13215,7 @@ function appendSecureLog(logPath, line) {
|
|
|
13427
13215
|
try {
|
|
13428
13216
|
const fd = openSync7(logPath, "a", 384);
|
|
13429
13217
|
try {
|
|
13430
|
-
|
|
13218
|
+
writeSync4(fd, `${(/* @__PURE__ */ new Date()).toISOString()} ${redacted}
|
|
13431
13219
|
`);
|
|
13432
13220
|
} finally {
|
|
13433
13221
|
closeSync7(fd);
|
|
@@ -14127,25 +13915,12 @@ function buildHttpProxyRoutes(providers, favorites, modelAliases = void 0, max =
|
|
|
14127
13915
|
}
|
|
14128
13916
|
|
|
14129
13917
|
// src/patcher.ts
|
|
14130
|
-
function getPatchManifestPath() {
|
|
14131
|
-
return join8(getAppHome(), "patch-state.json");
|
|
14132
|
-
}
|
|
14133
13918
|
function getPatchLockPath() {
|
|
14134
|
-
return
|
|
14135
|
-
}
|
|
14136
|
-
function readPatchManifest(path = getPatchManifestPath()) {
|
|
14137
|
-
try {
|
|
14138
|
-
const parsed = JSON.parse(readFileSync10(path, "utf8"));
|
|
14139
|
-
if (parsed && typeof parsed.binaryPath === "string" && typeof parsed.configHash === "string") {
|
|
14140
|
-
return parsed;
|
|
14141
|
-
}
|
|
14142
|
-
} catch {
|
|
14143
|
-
}
|
|
14144
|
-
return null;
|
|
13919
|
+
return join7(getAppHome(), "patch.lock");
|
|
14145
13920
|
}
|
|
14146
13921
|
function writePatchManifest(manifest, path = getPatchManifestPath()) {
|
|
14147
13922
|
mkdirSync5(getAppHome(), { recursive: true, mode: 448 });
|
|
14148
|
-
|
|
13923
|
+
writeFileSync5(path, `${JSON.stringify(manifest, null, 2)}
|
|
14149
13924
|
`, { encoding: "utf8", mode: 384 });
|
|
14150
13925
|
}
|
|
14151
13926
|
function buildPatchModelConfig(favorites, aliases, modelMetaFor) {
|
|
@@ -14220,7 +13995,7 @@ function computePatchConfigHash(config, transformsVersion = PATCH_TRANSFORMS_VER
|
|
|
14220
13995
|
if (localPatchIdentity !== void 0) {
|
|
14221
13996
|
payload.push(["local-patches", localPatchIdentity]);
|
|
14222
13997
|
}
|
|
14223
|
-
return
|
|
13998
|
+
return createHash8("sha256").update(JSON.stringify(payload)).digest("hex");
|
|
14224
13999
|
}
|
|
14225
14000
|
function buildDesiredPatchConfig(options = {}) {
|
|
14226
14001
|
const prefs = loadPreferences();
|
|
@@ -14298,12 +14073,12 @@ function pidIsAlive(pid) {
|
|
|
14298
14073
|
function tryAcquirePatchLock(lockPath = getPatchLockPath(), opts = {}) {
|
|
14299
14074
|
const now = opts.now ?? Date.now();
|
|
14300
14075
|
const isAlive = opts.isAlive ?? pidIsAlive;
|
|
14301
|
-
mkdirSync5(
|
|
14076
|
+
mkdirSync5(join7(lockPath, ".."), { recursive: true, mode: 448 });
|
|
14302
14077
|
for (let attempt = 0; attempt < 2; attempt++) {
|
|
14303
14078
|
try {
|
|
14304
14079
|
const fd = openSync8(lockPath, "wx");
|
|
14305
14080
|
const content = { pid: process.pid, startedAt: now };
|
|
14306
|
-
|
|
14081
|
+
writeFileSync5(fd, JSON.stringify(content));
|
|
14307
14082
|
closeSync8(fd);
|
|
14308
14083
|
return () => {
|
|
14309
14084
|
try {
|
|
@@ -14314,7 +14089,7 @@ function tryAcquirePatchLock(lockPath = getPatchLockPath(), opts = {}) {
|
|
|
14314
14089
|
} catch {
|
|
14315
14090
|
let stale = false;
|
|
14316
14091
|
try {
|
|
14317
|
-
const existing = JSON.parse(
|
|
14092
|
+
const existing = JSON.parse(readFileSync9(lockPath, "utf8"));
|
|
14318
14093
|
stale = !existing.pid || !isAlive(existing.pid) || typeof existing.startedAt === "number" && now - existing.startedAt > PATCH_LOCK_STALE_MS;
|
|
14319
14094
|
} catch {
|
|
14320
14095
|
stale = true;
|
|
@@ -14336,11 +14111,11 @@ function takeIgnoredLaunchOverride() {
|
|
|
14336
14111
|
}
|
|
14337
14112
|
function resolveClaudeBinaryForPatch() {
|
|
14338
14113
|
const envOverride = process.env["TWEAKCC_CC_INSTALLATION_PATH"]?.trim() || null;
|
|
14339
|
-
const nativeSymlink =
|
|
14340
|
-
if (envOverride && !
|
|
14114
|
+
const nativeSymlink = join7(homedir2(), ".local", "bin", "claude");
|
|
14115
|
+
if (envOverride && !existsSync4(envOverride)) {
|
|
14341
14116
|
return { ok: false, reason: "patch-target-missing", declaredPath: envOverride };
|
|
14342
14117
|
}
|
|
14343
|
-
const source = envOverride || (
|
|
14118
|
+
const source = envOverride || (existsSync4(nativeSymlink) ? nativeSymlink : null) || findClaudeBinary();
|
|
14344
14119
|
if (!source) return { ok: false, reason: "binary-not-found" };
|
|
14345
14120
|
let resolved;
|
|
14346
14121
|
try {
|
|
@@ -14372,7 +14147,7 @@ function resolveClaudeBinaryForPatch() {
|
|
|
14372
14147
|
}
|
|
14373
14148
|
ignoredLaunchOverride = launchOverrideResolved && launchOverrideResolved !== resolved ? { used: resolved, ignored: launchOverride } : null;
|
|
14374
14149
|
try {
|
|
14375
|
-
if (!
|
|
14150
|
+
if (!statSync7(resolved).isFile()) return { ok: false, reason: "binary-not-found" };
|
|
14376
14151
|
} catch {
|
|
14377
14152
|
return { ok: false, reason: "binary-not-found" };
|
|
14378
14153
|
}
|
|
@@ -14433,10 +14208,10 @@ function publishFileByRename(from, to, mode) {
|
|
|
14433
14208
|
try {
|
|
14434
14209
|
copyFileSync(from, temp);
|
|
14435
14210
|
if (mode !== void 0) chmodSync4(temp, mode);
|
|
14436
|
-
|
|
14211
|
+
renameSync2(temp, to);
|
|
14437
14212
|
} catch (err) {
|
|
14438
14213
|
try {
|
|
14439
|
-
|
|
14214
|
+
rmSync(temp, { force: true });
|
|
14440
14215
|
} catch {
|
|
14441
14216
|
}
|
|
14442
14217
|
throw err;
|
|
@@ -14504,8 +14279,8 @@ async function applyPatch(binaryPath, version, desired, configHash, opts) {
|
|
|
14504
14279
|
try {
|
|
14505
14280
|
mkdirSync5(backupDir(), { recursive: true });
|
|
14506
14281
|
const { tryDetectInstallation, readContent, writeContent } = await import("tweakcc");
|
|
14507
|
-
candidateDir = mkdtempSync(
|
|
14508
|
-
const candidatePath =
|
|
14282
|
+
candidateDir = mkdtempSync(join7(dirname5(binaryPath), ".clodex-patch-"));
|
|
14283
|
+
const candidatePath = join7(candidateDir, basename(binaryPath));
|
|
14509
14284
|
const seedCandidate = async (from) => {
|
|
14510
14285
|
copyFileSync(from, candidatePath);
|
|
14511
14286
|
const shim = shimEntryModuleName(candidatePath);
|
|
@@ -14652,9 +14427,9 @@ async function applyPatch(binaryPath, version, desired, configHash, opts) {
|
|
|
14652
14427
|
}
|
|
14653
14428
|
if (writeShim) restoreEntryModuleName(candidatePath, writeShim, { resign: true });
|
|
14654
14429
|
else if (publishedBlob) resignMachOBinary(candidatePath);
|
|
14655
|
-
patchedSize =
|
|
14430
|
+
patchedSize = statSync7(candidatePath).size;
|
|
14656
14431
|
patchedSha256 = sha256File(candidatePath);
|
|
14657
|
-
|
|
14432
|
+
renameSync2(candidatePath, binaryPath);
|
|
14658
14433
|
} catch (err) {
|
|
14659
14434
|
const detailLines = err instanceof PatchApplyError ? summarizePatchResults(err.results) : [];
|
|
14660
14435
|
if (opts.trace && detailLines.length) {
|
|
@@ -14669,7 +14444,7 @@ async function applyPatch(binaryPath, version, desired, configHash, opts) {
|
|
|
14669
14444
|
} finally {
|
|
14670
14445
|
if (candidateDir !== void 0) {
|
|
14671
14446
|
try {
|
|
14672
|
-
|
|
14447
|
+
rmSync(candidateDir, { recursive: true, force: true });
|
|
14673
14448
|
} catch (err) {
|
|
14674
14449
|
if (opts.trace) {
|
|
14675
14450
|
process.stderr.write(
|
|
@@ -14743,7 +14518,7 @@ function runRestoreCommand(target) {
|
|
|
14743
14518
|
}
|
|
14744
14519
|
if (manifest && manifest.backupPath && manifest.binaryPath === binaryPath && manifest.claudeVersion !== version && manifest.backupPath !== plan.backupPath) {
|
|
14745
14520
|
try {
|
|
14746
|
-
if (
|
|
14521
|
+
if (existsSync4(manifest.backupPath)) {
|
|
14747
14522
|
recordBackupProvenance(manifest.backupPath, manifest.binaryPath, {
|
|
14748
14523
|
assumed: manifest.pristineProvenance === "assumed"
|
|
14749
14524
|
});
|
|
@@ -14772,7 +14547,7 @@ function runRestoreCommand(target) {
|
|
|
14772
14547
|
}
|
|
14773
14548
|
let publishedMode;
|
|
14774
14549
|
try {
|
|
14775
|
-
publishedMode =
|
|
14550
|
+
publishedMode = statSync7(binaryPath).mode;
|
|
14776
14551
|
} catch {
|
|
14777
14552
|
}
|
|
14778
14553
|
try {
|
|
@@ -14831,7 +14606,7 @@ async function runPatchCommand(opts = {}) {
|
|
|
14831
14606
|
binaryPath,
|
|
14832
14607
|
claudeVersion: version,
|
|
14833
14608
|
configHash,
|
|
14834
|
-
binarySize:
|
|
14609
|
+
binarySize: statSync7(binaryPath).size
|
|
14835
14610
|
});
|
|
14836
14611
|
if (state === "current") {
|
|
14837
14612
|
p2.log.success(`claude ${version} is already patched with the current model config \u2014 nothing to do.`);
|
|
@@ -14844,7 +14619,7 @@ async function runPatchCommand(opts = {}) {
|
|
|
14844
14619
|
}
|
|
14845
14620
|
if (manifest && manifest.backupPath && (manifest.binaryPath !== binaryPath || manifest.claudeVersion !== version)) {
|
|
14846
14621
|
try {
|
|
14847
|
-
if (
|
|
14622
|
+
if (existsSync4(manifest.backupPath)) {
|
|
14848
14623
|
recordBackupProvenance(manifest.backupPath, manifest.binaryPath, {
|
|
14849
14624
|
assumed: manifest.pristineProvenance === "assumed"
|
|
14850
14625
|
});
|
|
@@ -14900,7 +14675,7 @@ async function runLaunchPatchCheck(opts = {}) {
|
|
|
14900
14675
|
binaryPath: resolved.binaryPath,
|
|
14901
14676
|
claudeVersion: resolved.version,
|
|
14902
14677
|
configHash,
|
|
14903
|
-
binarySize:
|
|
14678
|
+
binarySize: statSync7(resolved.binaryPath).size
|
|
14904
14679
|
});
|
|
14905
14680
|
if (state === "current") return;
|
|
14906
14681
|
const interactive = !opts.dryRun && !opts.agentStdout && process.stdin.isTTY === true && process.stdout.isTTY === true;
|
|
@@ -15237,16 +15012,16 @@ function localProvidersToServerModels(localProviders) {
|
|
|
15237
15012
|
import { randomUUID as randomUUID5 } from "crypto";
|
|
15238
15013
|
import {
|
|
15239
15014
|
closeSync as closeSync9,
|
|
15240
|
-
existsSync as
|
|
15015
|
+
existsSync as existsSync5,
|
|
15241
15016
|
fstatSync as fstatSync3,
|
|
15242
15017
|
fsyncSync as fsyncSync2,
|
|
15243
15018
|
lstatSync,
|
|
15244
15019
|
mkdirSync as mkdirSync6,
|
|
15245
15020
|
openSync as openSync9,
|
|
15246
|
-
readFileSync as
|
|
15247
|
-
renameSync as
|
|
15021
|
+
readFileSync as readFileSync10,
|
|
15022
|
+
renameSync as renameSync3,
|
|
15248
15023
|
unlinkSync as unlinkSync4,
|
|
15249
|
-
writeFileSync as
|
|
15024
|
+
writeFileSync as writeFileSync6
|
|
15250
15025
|
} from "fs";
|
|
15251
15026
|
import { dirname as dirname6 } from "path";
|
|
15252
15027
|
var JOURNAL_SCHEMA_VERSION = 1;
|
|
@@ -15327,7 +15102,7 @@ function parseJournal(raw) {
|
|
|
15327
15102
|
};
|
|
15328
15103
|
}
|
|
15329
15104
|
function readJournalUnlocked(path) {
|
|
15330
|
-
if (!
|
|
15105
|
+
if (!existsSync5(path)) return emptyJournal();
|
|
15331
15106
|
let fd;
|
|
15332
15107
|
try {
|
|
15333
15108
|
const before = lstatSync(path);
|
|
@@ -15350,7 +15125,7 @@ function readJournalUnlocked(path) {
|
|
|
15350
15125
|
if (opened.size > MAX_JOURNAL_BYTES) {
|
|
15351
15126
|
throw new Error("Credential cleanup journal is too large.");
|
|
15352
15127
|
}
|
|
15353
|
-
return parseJournal(JSON.parse(
|
|
15128
|
+
return parseJournal(JSON.parse(readFileSync10(fd, "utf8")));
|
|
15354
15129
|
} catch (error) {
|
|
15355
15130
|
const message = error instanceof Error ? error.message : String(error);
|
|
15356
15131
|
throw new Error(`Could not read credential cleanup journal: ${message}`);
|
|
@@ -15378,13 +15153,13 @@ function writeJournalUnlocked(journal, path) {
|
|
|
15378
15153
|
let fd;
|
|
15379
15154
|
try {
|
|
15380
15155
|
fd = openSync9(tmp, "wx", FILE_MODE4);
|
|
15381
|
-
|
|
15156
|
+
writeFileSync6(fd, `${JSON.stringify(journal, null, 2)}
|
|
15382
15157
|
`);
|
|
15383
15158
|
fsyncSync2(fd);
|
|
15384
15159
|
closeSync9(fd);
|
|
15385
15160
|
fd = void 0;
|
|
15386
15161
|
assertRegistryWriteOwnership(path);
|
|
15387
|
-
|
|
15162
|
+
renameSync3(tmp, path);
|
|
15388
15163
|
syncParentDirectory(path);
|
|
15389
15164
|
} finally {
|
|
15390
15165
|
if (fd !== void 0) closeSync9(fd);
|
|
@@ -19363,8 +19138,8 @@ import { createBrotliDecompress, createGunzip, createInflate } from "zlib";
|
|
|
19363
19138
|
|
|
19364
19139
|
// src/http-proxy/ca.ts
|
|
19365
19140
|
import { randomBytes as randomBytes2 } from "crypto";
|
|
19366
|
-
import { chmodSync as chmodSync5, existsSync as
|
|
19367
|
-
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";
|
|
19368
19143
|
import forge from "node-forge";
|
|
19369
19144
|
var CERT_DIR = "http-proxy";
|
|
19370
19145
|
var CA_CERT_FILE = "clodex-ca.pem";
|
|
@@ -19379,22 +19154,22 @@ function serialNumber() {
|
|
|
19379
19154
|
return bytes.toString("hex");
|
|
19380
19155
|
}
|
|
19381
19156
|
function certPaths() {
|
|
19382
|
-
const dir =
|
|
19157
|
+
const dir = join8(getAppHome(), CERT_DIR);
|
|
19383
19158
|
return {
|
|
19384
19159
|
dir,
|
|
19385
|
-
caCert:
|
|
19386
|
-
caKey:
|
|
19387
|
-
serverCert:
|
|
19388
|
-
serverKey:
|
|
19389
|
-
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)
|
|
19390
19165
|
};
|
|
19391
19166
|
}
|
|
19392
19167
|
function writePrivate(path, value) {
|
|
19393
|
-
|
|
19168
|
+
writeFileSync7(path, value, { encoding: "utf8", mode: 384 });
|
|
19394
19169
|
chmodSync5(path, 384);
|
|
19395
19170
|
}
|
|
19396
19171
|
function writePublic(path, value) {
|
|
19397
|
-
|
|
19172
|
+
writeFileSync7(path, value, { encoding: "utf8", mode: 420 });
|
|
19398
19173
|
chmodSync5(path, 420);
|
|
19399
19174
|
}
|
|
19400
19175
|
function generateCertificates(paths) {
|
|
@@ -19439,8 +19214,8 @@ function generateCertificates(paths) {
|
|
|
19439
19214
|
}
|
|
19440
19215
|
function storedCertificatesAreCurrent(paths) {
|
|
19441
19216
|
try {
|
|
19442
|
-
const ca = forge.pki.certificateFromPem(
|
|
19443
|
-
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"));
|
|
19444
19219
|
const now = Date.now();
|
|
19445
19220
|
const renewalBuffer = 7 * 24 * 60 * 60 * 1e3;
|
|
19446
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);
|
|
@@ -19451,13 +19226,13 @@ function storedCertificatesAreCurrent(paths) {
|
|
|
19451
19226
|
function ensureHttpProxyCertificates() {
|
|
19452
19227
|
const paths = certPaths();
|
|
19453
19228
|
const required = [paths.caCert, paths.caKey, paths.serverCert, paths.serverKey, paths.version];
|
|
19454
|
-
const current = required.every(
|
|
19229
|
+
const current = required.every(existsSync6) && readFileSync11(paths.version, "utf8") === CERT_VERSION && storedCertificatesAreCurrent(paths);
|
|
19455
19230
|
if (!current) generateCertificates(paths);
|
|
19456
19231
|
return {
|
|
19457
19232
|
caCertPath: paths.caCert,
|
|
19458
|
-
caCert:
|
|
19459
|
-
serverCert:
|
|
19460
|
-
serverKey:
|
|
19233
|
+
caCert: readFileSync11(paths.caCert, "utf8"),
|
|
19234
|
+
serverCert: readFileSync11(paths.serverCert, "utf8"),
|
|
19235
|
+
serverKey: readFileSync11(paths.serverKey, "utf8")
|
|
19461
19236
|
};
|
|
19462
19237
|
}
|
|
19463
19238
|
function ensureHttpProxyCaBundle(relayCaCertPath, additionalCaCertPath, onWarning) {
|
|
@@ -19471,7 +19246,7 @@ function ensureHttpProxyCaBundle(relayCaCertPath, additionalCaCertPath, onWarnin
|
|
|
19471
19246
|
let additionalCa;
|
|
19472
19247
|
try {
|
|
19473
19248
|
if (resolve3(additionalCaCertPath) === resolve3(relayCaCertPath)) return relayCaCertPath;
|
|
19474
|
-
additionalCa =
|
|
19249
|
+
additionalCa = readFileSync11(additionalCaCertPath, "utf8").trim();
|
|
19475
19250
|
} catch (err) {
|
|
19476
19251
|
return warn(
|
|
19477
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.`
|
|
@@ -19481,8 +19256,8 @@ function ensureHttpProxyCaBundle(relayCaCertPath, additionalCaCertPath, onWarnin
|
|
|
19481
19256
|
return warn(`NODE_EXTRA_CA_CERTS=${additionalCaCertPath} is empty, so it adds nothing.`);
|
|
19482
19257
|
}
|
|
19483
19258
|
try {
|
|
19484
|
-
const relayCa =
|
|
19485
|
-
const combinedPath =
|
|
19259
|
+
const relayCa = readFileSync11(relayCaCertPath, "utf8").trimEnd();
|
|
19260
|
+
const combinedPath = join8(dirname7(relayCaCertPath), "combined-ca.pem");
|
|
19486
19261
|
writePublic(combinedPath, `${relayCa}
|
|
19487
19262
|
${additionalCa}
|
|
19488
19263
|
`);
|
|
@@ -20622,6 +20397,7 @@ async function runHttpProxyServerCommand(debug = false, webSocketDiagnostics = f
|
|
|
20622
20397
|
if (handle.webSocketDiagnosticsLogPath) {
|
|
20623
20398
|
console.log(` WebSocket diagnostics: ${handle.webSocketDiagnosticsLogPath}`);
|
|
20624
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."));
|
|
20625
20401
|
}
|
|
20626
20402
|
console.log("");
|
|
20627
20403
|
printHttpProxyModels(loaded.routes, loaded.aliases);
|
|
@@ -21001,6 +20777,7 @@ async function runServerCommand(options = {}) {
|
|
|
21001
20777
|
if (webSocketDiagnosticsLogPath) {
|
|
21002
20778
|
console.log(` WebSocket diagnostics: ${webSocketDiagnosticsLogPath}`);
|
|
21003
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."));
|
|
21004
20781
|
}
|
|
21005
20782
|
if (mode === "network") {
|
|
21006
20783
|
for (const { name, address } of getLocalIps()) {
|
|
@@ -21290,6 +21067,227 @@ function planLaunchWizard(opts) {
|
|
|
21290
21067
|
return { skip: false, target: null };
|
|
21291
21068
|
}
|
|
21292
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
|
+
|
|
21293
21291
|
// src/cli.ts
|
|
21294
21292
|
var STARTER_CLAUDE_FLAGS = /* @__PURE__ */ new Set(["--dry-run", "--trace", "--fast", "--endpoint", "--proxy", "--save-mode", "--help", "-h", "--version", "-v"]);
|
|
21295
21293
|
var CLODEX_LAUNCH_FLAGS = /* @__PURE__ */ new Set(["--provider", "--model", "--context"]);
|
|
@@ -21511,6 +21509,15 @@ function parseArgs(args) {
|
|
|
21511
21509
|
}
|
|
21512
21510
|
return parsed2;
|
|
21513
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
|
+
}
|
|
21514
21521
|
if (first !== "claude") {
|
|
21515
21522
|
return {
|
|
21516
21523
|
...emptyParsed("root"),
|
|
@@ -21556,6 +21563,7 @@ ${pc13.bold("Usage:")}
|
|
|
21556
21563
|
clodex models
|
|
21557
21564
|
clodex favorites
|
|
21558
21565
|
clodex providers
|
|
21566
|
+
clodex install-vscode-launcher
|
|
21559
21567
|
clodex --help
|
|
21560
21568
|
clodex --version
|
|
21561
21569
|
|
|
@@ -21570,6 +21578,9 @@ ${pc13.bold("Commands:")}
|
|
|
21570
21578
|
models Manage favorite models and aliases (max ${MAX_MODEL_CATALOG})
|
|
21571
21579
|
favorites Alias for models
|
|
21572
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
|
|
21573
21584
|
|
|
21574
21585
|
${pc13.bold("Bridge modes (claude and server):")}
|
|
21575
21586
|
--endpoint Local Anthropic-format gateway; Claude Code launches with
|
|
@@ -21676,8 +21687,9 @@ ${pc13.bold("Common options (both modes):")}
|
|
|
21676
21687
|
~/.clodex/server-runtime.json, so the
|
|
21677
21688
|
clodex-claude wrapper never bridges to it
|
|
21678
21689
|
(CLODEX_NO_DISCOVERY=1 works too)
|
|
21679
|
-
--ws-diagnostics Log sanitized request envelopes
|
|
21680
|
-
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)
|
|
21681
21693
|
--help, --version Help / version
|
|
21682
21694
|
|
|
21683
21695
|
${pc13.bold("Endpoint mode only")} ${pc13.dim("(error if combined with --proxy)")}:
|
|
@@ -21810,6 +21822,42 @@ ${pc13.bold("Behavior:")}
|
|
|
21810
21822
|
permissions. Local failures are reported but never block the built-ins.
|
|
21811
21823
|
Run clodex patch again after every claude update.`;
|
|
21812
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
|
+
}
|
|
21813
21861
|
function printHelp(text5) {
|
|
21814
21862
|
console.log(`
|
|
21815
21863
|
${text5}
|
|
@@ -22719,6 +22767,17 @@ Error: ${resolved.error}
|
|
|
22719
22767
|
localPatches: parsed.patchLocalPatches
|
|
22720
22768
|
});
|
|
22721
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
|
+
}
|
|
22722
22781
|
if (parsed.showVersion) {
|
|
22723
22782
|
console.log(VERSION);
|
|
22724
22783
|
return 0;
|
|
@@ -22741,7 +22800,7 @@ Error: ${resolved.error}
|
|
|
22741
22800
|
function isCliEntryPoint() {
|
|
22742
22801
|
if (!process.argv[1]) return false;
|
|
22743
22802
|
try {
|
|
22744
|
-
return realpathSync3(
|
|
22803
|
+
return realpathSync3(fileURLToPath2(import.meta.url)) === realpathSync3(process.argv[1]);
|
|
22745
22804
|
} catch {
|
|
22746
22805
|
return false;
|
|
22747
22806
|
}
|
|
@@ -22759,6 +22818,7 @@ if (isCliEntryPoint()) {
|
|
|
22759
22818
|
}
|
|
22760
22819
|
export {
|
|
22761
22820
|
claudeHelpText,
|
|
22821
|
+
installVscodeLauncherHelpText,
|
|
22762
22822
|
main,
|
|
22763
22823
|
modelsHelpText,
|
|
22764
22824
|
parseArgs,
|