@bman654/clodex 2.8.0 → 2.8.1
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 +4 -0
- package/dist/cli.js +219 -9
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -205,6 +205,10 @@ Enabling this feature executes that JavaScript with your full user permissions;
|
|
|
205
205
|
|
|
206
206
|
The module must default-export an array. Each site has a unique lowercase `id` and an `apply` function that returns the complete source and emits the generated `marker` exactly once when it applies:
|
|
207
207
|
|
|
208
|
+
The `source` your `apply` receives is every JavaScript module in the binary joined together, with a ``/*clodex:module-boundary`;{}"]*/`` line between them. This is true of every Claude Code version — a pre-2.1.242 binary carries the bundle plus five small helper modules; a 2.1.242-or-later one carries the bundle split across roughly 1,370. Match and replace as you always have; just do not add or remove one of those lines, or the whole patch is refused. (The punctuation in the marker is there so that a wildcard bounded by a character class cannot run across a module boundary.)
|
|
209
|
+
|
|
210
|
+
If your transform works by **position** rather than by matching an anchor, mind where it lands: text prepended to `source` goes into the first module and text appended to it goes into the last, and neither is necessarily the module that runs at startup. Anchor your edit to text you match, the way the examples below do.
|
|
211
|
+
|
|
208
212
|
```js
|
|
209
213
|
export default [
|
|
210
214
|
{
|
package/dist/cli.js
CHANGED
|
@@ -382,7 +382,7 @@ import { join } from "path";
|
|
|
382
382
|
// package.json
|
|
383
383
|
var package_default = {
|
|
384
384
|
name: "@bman654/clodex",
|
|
385
|
-
version: "2.8.
|
|
385
|
+
version: "2.8.1",
|
|
386
386
|
publishConfig: {
|
|
387
387
|
access: "public"
|
|
388
388
|
},
|
|
@@ -3419,7 +3419,7 @@ function savedStopsAfter(current, assignments) {
|
|
|
3419
3419
|
}
|
|
3420
3420
|
|
|
3421
3421
|
// src/patch-transforms.ts
|
|
3422
|
-
var PATCH_TRANSFORMS_VERSION =
|
|
3422
|
+
var PATCH_TRANSFORMS_VERSION = 9;
|
|
3423
3423
|
var NATIVE_EFFORT_LEVELS = ["low", "medium", "high", "xhigh", "max"];
|
|
3424
3424
|
var BASE_EFFORT_LEVELS = ["low", "medium", "high"];
|
|
3425
3425
|
function projectNativeEffort(effort) {
|
|
@@ -3622,7 +3622,7 @@ function applyClodexPatches(source, config) {
|
|
|
3622
3622
|
}).join("; ");
|
|
3623
3623
|
applyOnce(
|
|
3624
3624
|
"PATCH 4: Agent tool model description",
|
|
3625
|
-
/(describe\(`Optional model override for this agent[
|
|
3625
|
+
/(describe\(`Optional model override for this agent(?:[^`\\]|\\.)*?)(`)/,
|
|
3626
3626
|
(_m, body, close) => body.includes("Additional custom models") ? body + close : body + " Additional custom models: " + listing + "." + close,
|
|
3627
3627
|
{ required: false, noopIsSkip: true }
|
|
3628
3628
|
);
|
|
@@ -4169,7 +4169,7 @@ function captureBuiltInPatchProofs(source, config, results) {
|
|
|
4169
4169
|
);
|
|
4170
4170
|
addPattern(
|
|
4171
4171
|
"PATCH 4: Agent tool model description",
|
|
4172
|
-
/describe\(`Optional model override for this agent[
|
|
4172
|
+
/describe\(`Optional model override for this agent(?:[^`\\]|\\.)*?`/
|
|
4173
4173
|
);
|
|
4174
4174
|
const aliases = configuredAliases(config);
|
|
4175
4175
|
if (protectsResult(results, "PATCH 6: alias resolver switch")) {
|
|
@@ -6800,6 +6800,9 @@ var BUN_OFFSETS_BYTES = 32;
|
|
|
6800
6800
|
var TAIL_SCAN_BYTES = 16 * 1024 * 1024;
|
|
6801
6801
|
var MODULE_STRUCT_BYTES_CURRENT = 52;
|
|
6802
6802
|
var MODULE_STRUCT_BYTES_LEGACY = 36;
|
|
6803
|
+
var CONTENTS_FIELD_AT = 8;
|
|
6804
|
+
var LOADER_FROM_END = 3;
|
|
6805
|
+
var BUN_JAVASCRIPT_LOADER = 1;
|
|
6803
6806
|
var MAX_MODULE_NAME_BYTES = 4096;
|
|
6804
6807
|
var MIN_SHIMMABLE_NAME_BYTES = 7;
|
|
6805
6808
|
function tweakccRecognizesModuleName(name) {
|
|
@@ -6854,6 +6857,8 @@ function parseBunModuleNamesAtUnchecked(fd, offsetsAt) {
|
|
|
6854
6857
|
if (!modules) return null;
|
|
6855
6858
|
const names = [];
|
|
6856
6859
|
const nameOffsets = [];
|
|
6860
|
+
const contents = [];
|
|
6861
|
+
const loaders = [];
|
|
6857
6862
|
for (let index = 0; index < moduleCount; index++) {
|
|
6858
6863
|
const nameOffset = modules.readUInt32LE(index * structBytes);
|
|
6859
6864
|
const nameLength = modules.readUInt32LE(index * structBytes + 4);
|
|
@@ -6865,8 +6870,23 @@ function parseBunModuleNamesAtUnchecked(fd, offsetsAt) {
|
|
|
6865
6870
|
if (!/^[\x20-\x7e]+$/.test(name)) return null;
|
|
6866
6871
|
names.push(name);
|
|
6867
6872
|
nameOffsets.push(blobAt + nameOffset);
|
|
6873
|
+
contents.push({
|
|
6874
|
+
offset: modules.readUInt32LE(index * structBytes + CONTENTS_FIELD_AT),
|
|
6875
|
+
length: modules.readUInt32LE(index * structBytes + CONTENTS_FIELD_AT + 4)
|
|
6876
|
+
});
|
|
6877
|
+
loaders.push(modules.readUInt8(index * structBytes + structBytes - LOADER_FROM_END));
|
|
6868
6878
|
}
|
|
6869
|
-
return {
|
|
6879
|
+
return {
|
|
6880
|
+
names,
|
|
6881
|
+
entryPointId,
|
|
6882
|
+
offsets: nameOffsets,
|
|
6883
|
+
contents,
|
|
6884
|
+
loaders,
|
|
6885
|
+
blobAt,
|
|
6886
|
+
modulesAt: blobAt + modulesOffset,
|
|
6887
|
+
structBytes,
|
|
6888
|
+
byteCount: Number(byteCount)
|
|
6889
|
+
};
|
|
6870
6890
|
}
|
|
6871
6891
|
function findStandIn(fd, fileSize, marker) {
|
|
6872
6892
|
const needle = Buffer.from(marker);
|
|
@@ -6912,6 +6932,70 @@ function isMachO(fd) {
|
|
|
6912
6932
|
const value = magic.readUInt32BE(0);
|
|
6913
6933
|
return value === 4277009102 || value === 4277009103 || value === 3472551422 || value === 3489328638 || value === 3405691582 || value === 3405691583;
|
|
6914
6934
|
}
|
|
6935
|
+
function readBunModuleTable(path) {
|
|
6936
|
+
const fd = openSync2(path, "r");
|
|
6937
|
+
try {
|
|
6938
|
+
const parsed = readBunModuleNames(fd, statSync4(path).size);
|
|
6939
|
+
if (!parsed) return null;
|
|
6940
|
+
const { names, loaders, contents, entryPointId, blobAt, modulesAt, structBytes, byteCount } = parsed;
|
|
6941
|
+
return { names, loaders, contents, entryPointId, blobAt, modulesAt, structBytes, byteCount };
|
|
6942
|
+
} finally {
|
|
6943
|
+
closeSync2(fd);
|
|
6944
|
+
}
|
|
6945
|
+
}
|
|
6946
|
+
function readBunJavaScriptModules(path) {
|
|
6947
|
+
const fd = openSync2(path, "r");
|
|
6948
|
+
try {
|
|
6949
|
+
const parsed = readBunModuleNames(fd, statSync4(path).size);
|
|
6950
|
+
if (!parsed) return null;
|
|
6951
|
+
const modules = [];
|
|
6952
|
+
for (let index = 0; index < parsed.names.length; index++) {
|
|
6953
|
+
if (parsed.loaders[index] !== BUN_JAVASCRIPT_LOADER) continue;
|
|
6954
|
+
const range = parsed.contents[index];
|
|
6955
|
+
if (range.offset < 0 || range.length < 0 || range.offset + range.length > parsed.byteCount) {
|
|
6956
|
+
return null;
|
|
6957
|
+
}
|
|
6958
|
+
const bytes = range.length === 0 ? Buffer.alloc(0) : readAt(fd, range.length, parsed.blobAt + range.offset);
|
|
6959
|
+
if (!bytes) return null;
|
|
6960
|
+
modules.push({
|
|
6961
|
+
index,
|
|
6962
|
+
name: parsed.names[index],
|
|
6963
|
+
source: bytes.toString("utf8"),
|
|
6964
|
+
byteLength: bytes.length
|
|
6965
|
+
});
|
|
6966
|
+
}
|
|
6967
|
+
return modules;
|
|
6968
|
+
} finally {
|
|
6969
|
+
closeSync2(fd);
|
|
6970
|
+
}
|
|
6971
|
+
}
|
|
6972
|
+
function repointBunModuleContents(path, edits) {
|
|
6973
|
+
if (edits.length === 0) return;
|
|
6974
|
+
const fd = openSync2(path, "r+");
|
|
6975
|
+
try {
|
|
6976
|
+
const parsed = readBunModuleNames(fd, statSync4(path).size);
|
|
6977
|
+
if (!parsed) throw new Error(`cannot read the Bun module table of ${path}`);
|
|
6978
|
+
for (const { index, range } of edits) {
|
|
6979
|
+
if (index < 0 || index >= parsed.names.length) {
|
|
6980
|
+
throw new Error(`module ${index} is outside the ${parsed.names.length}-module table of ${path}`);
|
|
6981
|
+
}
|
|
6982
|
+
if (range.offset < 0 || range.length < 0 || range.offset + range.length > parsed.byteCount) {
|
|
6983
|
+
throw new Error(
|
|
6984
|
+
`module ${index} would point at ${range.offset}+${range.length}, outside the ${parsed.byteCount}-byte blob of ${path}`
|
|
6985
|
+
);
|
|
6986
|
+
}
|
|
6987
|
+
const at = parsed.modulesAt + index * parsed.structBytes + CONTENTS_FIELD_AT;
|
|
6988
|
+
const pair = Buffer.alloc(8);
|
|
6989
|
+
pair.writeUInt32LE(range.offset, 0);
|
|
6990
|
+
pair.writeUInt32LE(range.length, 4);
|
|
6991
|
+
if (writeSync2(fd, pair, 0, pair.length, at) !== pair.length) {
|
|
6992
|
+
throw new Error(`short write repointing module ${index} of ${path}`);
|
|
6993
|
+
}
|
|
6994
|
+
}
|
|
6995
|
+
} finally {
|
|
6996
|
+
closeSync2(fd);
|
|
6997
|
+
}
|
|
6998
|
+
}
|
|
6915
6999
|
function shimEntryModuleName(path) {
|
|
6916
7000
|
const fd = openSync2(path, "r+");
|
|
6917
7001
|
try {
|
|
@@ -6944,7 +7028,17 @@ function restoreEntryModuleName(path, shim, { resign }) {
|
|
|
6944
7028
|
}
|
|
6945
7029
|
writeNameBytes(fd, shim.original, offset);
|
|
6946
7030
|
restoreEveryStandIn(fd, statSync4(path).size, shim);
|
|
6947
|
-
machO = resign
|
|
7031
|
+
machO = resign;
|
|
7032
|
+
} finally {
|
|
7033
|
+
closeSync2(fd);
|
|
7034
|
+
}
|
|
7035
|
+
if (machO) resignMachOBinary(path);
|
|
7036
|
+
}
|
|
7037
|
+
function resignMachOBinary(path) {
|
|
7038
|
+
const fd = openSync2(path, "r");
|
|
7039
|
+
let machO;
|
|
7040
|
+
try {
|
|
7041
|
+
machO = isMachO(fd);
|
|
6948
7042
|
} finally {
|
|
6949
7043
|
closeSync2(fd);
|
|
6950
7044
|
}
|
|
@@ -6953,6 +7047,99 @@ function restoreEntryModuleName(path, shim, { resign }) {
|
|
|
6953
7047
|
}
|
|
6954
7048
|
}
|
|
6955
7049
|
|
|
7050
|
+
// src/bun-bundle.ts
|
|
7051
|
+
function writableModuleIndex(path) {
|
|
7052
|
+
const table = readBunModuleTable(path);
|
|
7053
|
+
if (!table) return null;
|
|
7054
|
+
const index = table.names.findIndex(tweakccRecognizesModuleName);
|
|
7055
|
+
return index < 0 ? null : index;
|
|
7056
|
+
}
|
|
7057
|
+
var BUNDLE_MODULE_SEPARATOR = '\n/*clodex:module-boundary`;{}"]*/\n';
|
|
7058
|
+
function readClaudeBundle(path) {
|
|
7059
|
+
const modules = readBunJavaScriptModules(path);
|
|
7060
|
+
if (!modules || modules.length === 0) return null;
|
|
7061
|
+
if (modules.some((module) => module.source.includes(BUNDLE_MODULE_SEPARATOR))) return null;
|
|
7062
|
+
if (modules.some((module) => !roundTripsAsUtf8(module.source, module.byteLength))) return null;
|
|
7063
|
+
return { modules, source: modules.map((module) => module.source).join(BUNDLE_MODULE_SEPARATOR) };
|
|
7064
|
+
}
|
|
7065
|
+
function roundTripsAsUtf8(source, byteLength) {
|
|
7066
|
+
return Buffer.byteLength(source, "utf8") === byteLength && !source.includes("\uFFFD");
|
|
7067
|
+
}
|
|
7068
|
+
function splitBundleSource(bundle, patched) {
|
|
7069
|
+
const parts = patched.split(BUNDLE_MODULE_SEPARATOR);
|
|
7070
|
+
if (parts.length !== bundle.modules.length) {
|
|
7071
|
+
throw new Error(
|
|
7072
|
+
`the patched bundle has ${parts.length} module boundaries, expected ${bundle.modules.length}`
|
|
7073
|
+
);
|
|
7074
|
+
}
|
|
7075
|
+
return parts;
|
|
7076
|
+
}
|
|
7077
|
+
function planBundleWrite(bundle, patchedSources, writableIndex) {
|
|
7078
|
+
if (patchedSources.length !== bundle.modules.length) {
|
|
7079
|
+
throw new Error(
|
|
7080
|
+
`expected ${bundle.modules.length} patched module sources, got ${patchedSources.length}`
|
|
7081
|
+
);
|
|
7082
|
+
}
|
|
7083
|
+
const writable = bundle.modules.findIndex((module) => module.index === writableIndex);
|
|
7084
|
+
if (writable < 0) {
|
|
7085
|
+
throw new Error(`module ${writableIndex} is not one of this binary's JavaScript modules`);
|
|
7086
|
+
}
|
|
7087
|
+
const changed = bundle.modules.map((module, at) => at).filter((at) => at !== writable && patchedSources[at] !== bundle.modules[at].source);
|
|
7088
|
+
if (changed.length === 0) return { content: patchedSources[writable], repoints: [] };
|
|
7089
|
+
const order = [writable, ...changed];
|
|
7090
|
+
const repoints = [];
|
|
7091
|
+
let content = "";
|
|
7092
|
+
let start = 0;
|
|
7093
|
+
for (const at of order) {
|
|
7094
|
+
const source = patchedSources[at];
|
|
7095
|
+
const length = Buffer.byteLength(source, "utf8");
|
|
7096
|
+
repoints.push({ index: bundle.modules[at].index, start, length, expected: source });
|
|
7097
|
+
content += source + "\0";
|
|
7098
|
+
start += length + 1;
|
|
7099
|
+
}
|
|
7100
|
+
return { content, repoints };
|
|
7101
|
+
}
|
|
7102
|
+
function applyBundleWritePlan(path, plan) {
|
|
7103
|
+
const table = readBunModuleTable(path);
|
|
7104
|
+
if (!table) {
|
|
7105
|
+
if (plan.repoints.length === 0) return;
|
|
7106
|
+
throw new Error(`cannot read the Bun module table of ${path} after repacking`);
|
|
7107
|
+
}
|
|
7108
|
+
const recognized = table.names.filter(tweakccRecognizesModuleName);
|
|
7109
|
+
if (recognized.length !== 1) {
|
|
7110
|
+
throw new Error(
|
|
7111
|
+
`${path} has ${recognized.length} modules tweakcc would write to; the patch was planned around exactly one`
|
|
7112
|
+
);
|
|
7113
|
+
}
|
|
7114
|
+
if (plan.repoints.length === 0) return;
|
|
7115
|
+
const writable = table.names.findIndex(tweakccRecognizesModuleName);
|
|
7116
|
+
if (writable !== plan.repoints[0].index) {
|
|
7117
|
+
throw new Error(
|
|
7118
|
+
`${path} exposes module ${writable} to tweakcc, but the patch was planned around module ${plan.repoints[0].index}`
|
|
7119
|
+
);
|
|
7120
|
+
}
|
|
7121
|
+
const written = table.contents[writable];
|
|
7122
|
+
const expectedBytes = Buffer.byteLength(plan.content, "utf8");
|
|
7123
|
+
if (written.length !== expectedBytes) {
|
|
7124
|
+
throw new Error(
|
|
7125
|
+
`${path} holds ${written.length} bytes where the ${expectedBytes} bytes clodex wrote should be; refusing to repoint its modules`
|
|
7126
|
+
);
|
|
7127
|
+
}
|
|
7128
|
+
const edits = plan.repoints.map((repoint) => ({
|
|
7129
|
+
index: repoint.index,
|
|
7130
|
+
range: { offset: written.offset + repoint.start, length: repoint.length }
|
|
7131
|
+
}));
|
|
7132
|
+
repointBunModuleContents(path, edits);
|
|
7133
|
+
const after = readBunJavaScriptModules(path);
|
|
7134
|
+
if (!after) throw new Error(`cannot re-read the modules of ${path} after repointing them`);
|
|
7135
|
+
const byIndex = new Map(after.map((module) => [module.index, module.source]));
|
|
7136
|
+
for (const repoint of plan.repoints) {
|
|
7137
|
+
if (byIndex.get(repoint.index) !== repoint.expected) {
|
|
7138
|
+
throw new Error(`module ${repoint.index} of ${path} did not read back as the patched source`);
|
|
7139
|
+
}
|
|
7140
|
+
}
|
|
7141
|
+
}
|
|
7142
|
+
|
|
6956
7143
|
// src/patch-backup.ts
|
|
6957
7144
|
import { createHash as createHash5 } from "crypto";
|
|
6958
7145
|
import { existsSync as existsSync4, readFileSync as readFileSync7, readdirSync, statSync as statSync5 } from "fs";
|
|
@@ -12304,9 +12491,15 @@ async function applyPatch(binaryPath, version, desired, configHash, opts) {
|
|
|
12304
12491
|
copyFileSync(from, candidatePath);
|
|
12305
12492
|
const shim = shimEntryModuleName(candidatePath);
|
|
12306
12493
|
const installation = await tryDetectInstallation({ path: candidatePath });
|
|
12307
|
-
const
|
|
12494
|
+
const bundle = readClaudeBundle(candidatePath);
|
|
12495
|
+
if (!bundle && installation.kind === "native") {
|
|
12496
|
+
p2.log.warn(
|
|
12497
|
+
`Could not read ${candidatePath} as a Bun module table, so only the module tweakcc names is being patched. On Claude Code 2.1.242 and later that module is a stub holding none of the code clodex patches, and the patch below will fail at its first required site \u2014 the cause is this read, not a changed anchor.`
|
|
12498
|
+
);
|
|
12499
|
+
}
|
|
12500
|
+
const source = bundle ? bundle.source : await readContent(installation);
|
|
12308
12501
|
if (shim) restoreEntryModuleName(candidatePath, shim, { resign: false });
|
|
12309
|
-
return { installation, source };
|
|
12502
|
+
return { installation, source, bundle };
|
|
12310
12503
|
};
|
|
12311
12504
|
const facts = collectPristineFacts({
|
|
12312
12505
|
version,
|
|
@@ -12399,8 +12592,25 @@ async function applyPatch(binaryPath, version, desired, configHash, opts) {
|
|
|
12399
12592
|
}
|
|
12400
12593
|
results = [...results, ...local.results];
|
|
12401
12594
|
const writeShim = shimEntryModuleName(candidatePath);
|
|
12402
|
-
|
|
12595
|
+
let repointed = false;
|
|
12596
|
+
if (loaded.bundle) {
|
|
12597
|
+
const writable = writableModuleIndex(candidatePath);
|
|
12598
|
+
if (writable === null) {
|
|
12599
|
+
throw new Error("no module of the patch candidate carries a name tweakcc can write to");
|
|
12600
|
+
}
|
|
12601
|
+
const plan2 = planBundleWrite(
|
|
12602
|
+
loaded.bundle,
|
|
12603
|
+
splitBundleSource(loaded.bundle, local.content),
|
|
12604
|
+
writable
|
|
12605
|
+
);
|
|
12606
|
+
await writeContent(loaded.installation, plan2.content);
|
|
12607
|
+
applyBundleWritePlan(candidatePath, plan2);
|
|
12608
|
+
repointed = plan2.repoints.length > 0;
|
|
12609
|
+
} else {
|
|
12610
|
+
await writeContent(loaded.installation, local.content);
|
|
12611
|
+
}
|
|
12403
12612
|
if (writeShim) restoreEntryModuleName(candidatePath, writeShim, { resign: true });
|
|
12613
|
+
else if (repointed) resignMachOBinary(candidatePath);
|
|
12404
12614
|
patchedSize = statSync6(candidatePath).size;
|
|
12405
12615
|
patchedSha256 = sha256File(candidatePath);
|
|
12406
12616
|
renameSync2(candidatePath, binaryPath);
|