@bman654/clodex 2.1.8 → 2.2.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 +27 -2
- package/dist/{chunk-VQOX3AXE.js → chunk-W4SVDQCZ.js} +7 -1
- package/dist/chunk-W4SVDQCZ.js.map +1 -0
- package/dist/claude-wrapper.js +1 -1
- package/dist/cli.js +542 -39
- package/dist/cli.js.map +1 -1
- package/package.json +2 -2
- package/dist/chunk-VQOX3AXE.js.map +0 -1
package/dist/cli.js
CHANGED
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
getCredentialMutationLockPath,
|
|
10
10
|
getCredentialStateRoot,
|
|
11
11
|
getInstalledClaudeVersion,
|
|
12
|
+
getLocalPatchesPath,
|
|
12
13
|
getLogsPath,
|
|
13
14
|
getSavedServerPassword,
|
|
14
15
|
getServerExposedProviders,
|
|
@@ -39,7 +40,7 @@ import {
|
|
|
39
40
|
withProviderMutationLock,
|
|
40
41
|
withRegistryWriteLock,
|
|
41
42
|
withRegistryWriteLockSync
|
|
42
|
-
} from "./chunk-
|
|
43
|
+
} from "./chunk-W4SVDQCZ.js";
|
|
43
44
|
|
|
44
45
|
// src/cli.ts
|
|
45
46
|
import pc13 from "picocolors";
|
|
@@ -217,7 +218,7 @@ import { join } from "path";
|
|
|
217
218
|
// package.json
|
|
218
219
|
var package_default = {
|
|
219
220
|
name: "@bman654/clodex",
|
|
220
|
-
version: "2.
|
|
221
|
+
version: "2.2.0",
|
|
221
222
|
publishConfig: {
|
|
222
223
|
access: "public"
|
|
223
224
|
},
|
|
@@ -273,7 +274,7 @@ var package_default = {
|
|
|
273
274
|
open: "11.0.0",
|
|
274
275
|
picocolors: "1.1.1",
|
|
275
276
|
tweakcc: "4.3.0",
|
|
276
|
-
undici: "7.
|
|
277
|
+
undici: "7.29.0",
|
|
277
278
|
ws: "8.21.0"
|
|
278
279
|
},
|
|
279
280
|
devDependencies: {
|
|
@@ -4523,20 +4524,28 @@ function continuationMismatchSummary(entry, payload, log12) {
|
|
|
4523
4524
|
const details = continuationMismatchDetails(entry, payload, log12, true);
|
|
4524
4525
|
return `full_items=${details.fullItems} expected_prefix_items=${details.expectedPrefixItems} first_mismatch=${details.firstMismatch} expected=${details.expectedKind} actual=${details.actualKind}`;
|
|
4525
4526
|
}
|
|
4526
|
-
function
|
|
4527
|
+
function canonicalItemStrings(items) {
|
|
4528
|
+
return items.map((item) => canonicalJson(normalizeToolCallJson([item])));
|
|
4529
|
+
}
|
|
4530
|
+
function isStrictPrefix(head, client) {
|
|
4531
|
+
if (client.length <= head.length) return false;
|
|
4532
|
+
for (let index = 0; index < head.length; index += 1) {
|
|
4533
|
+
if (head[index] !== client[index]) return false;
|
|
4534
|
+
}
|
|
4535
|
+
return true;
|
|
4536
|
+
}
|
|
4537
|
+
function continuationMatch(entry, payload, clientItems) {
|
|
4527
4538
|
if (!entry.responseId || !entry.requestInput || !entry.expectedAssistant) return void 0;
|
|
4528
4539
|
const full = inputArray(payload);
|
|
4529
|
-
|
|
4530
|
-
if (
|
|
4531
|
-
return { delta: full.slice(
|
|
4540
|
+
entry.canonicalPrefix ??= canonicalItemStrings([...entry.requestInput, ...entry.expectedAssistant]);
|
|
4541
|
+
if (isStrictPrefix(entry.canonicalPrefix, clientItems)) {
|
|
4542
|
+
return { delta: full.slice(entry.canonicalPrefix.length), mode: "exact" };
|
|
4532
4543
|
}
|
|
4533
4544
|
const echoedAssistant = entry.expectedAssistant.filter((item) => conversationItemKind(item) !== "reasoning");
|
|
4534
4545
|
if (echoedAssistant.length === entry.expectedAssistant.length) return void 0;
|
|
4535
|
-
|
|
4536
|
-
if (
|
|
4537
|
-
|
|
4538
|
-
}
|
|
4539
|
-
return { delta: full.slice(echoablePrefix.length), mode: "omitted_reasoning" };
|
|
4546
|
+
entry.canonicalEchoablePrefix ??= canonicalItemStrings([...entry.requestInput, ...echoedAssistant]);
|
|
4547
|
+
if (!isStrictPrefix(entry.canonicalEchoablePrefix, clientItems)) return void 0;
|
|
4548
|
+
return { delta: full.slice(entry.canonicalEchoablePrefix.length), mode: "omitted_reasoning" };
|
|
4540
4549
|
}
|
|
4541
4550
|
function eventType(event) {
|
|
4542
4551
|
return event && typeof event === "object" && typeof event.type === "string" ? event.type : void 0;
|
|
@@ -5156,6 +5165,8 @@ function handleSocketMessage(entry, data) {
|
|
|
5156
5165
|
entry.responseId = ctx.responseId;
|
|
5157
5166
|
entry.requestInput = inputArray(ctx.originalPayload);
|
|
5158
5167
|
entry.expectedAssistant = expectedAssistantItems(ctx);
|
|
5168
|
+
entry.canonicalPrefix = void 0;
|
|
5169
|
+
entry.canonicalEchoablePrefix = void 0;
|
|
5159
5170
|
entry.promptFieldHashes = ctx.promptFieldHashes;
|
|
5160
5171
|
entry.instructionsSnapshot = ctx.instructionsSnapshot;
|
|
5161
5172
|
entry.lastUsedAt = now;
|
|
@@ -5318,7 +5329,8 @@ function createResponsesWebSocketFetch(wsUrl, log12, options = {}) {
|
|
|
5318
5329
|
const evictions = cleanupExpiredConnections(now);
|
|
5319
5330
|
const candidates = partitionKey ? connectionEntries(partitionKey) : [];
|
|
5320
5331
|
const idleCandidates = candidates.filter((entry) => !entry.inFlight);
|
|
5321
|
-
const
|
|
5332
|
+
const clientItems = idleCandidates.length ? canonicalItemStrings(inputArray(payload)) : [];
|
|
5333
|
+
const matches = idleCandidates.map((entry) => ({ entry, match: continuationMatch(entry, payload, clientItems) })).filter((candidate) => candidate.match !== void 0).sort((left, right) => left.match.delta.length - right.match.delta.length || (left.match.mode === right.match.mode ? 0 : left.match.mode === "exact" ? -1 : 1));
|
|
5322
5334
|
let selected = matches[0]?.entry;
|
|
5323
5335
|
const selectedMatch = matches[0]?.match;
|
|
5324
5336
|
const selectedDelta = selectedMatch?.delta;
|
|
@@ -13906,16 +13918,16 @@ function planLaunchWizard(opts) {
|
|
|
13906
13918
|
}
|
|
13907
13919
|
|
|
13908
13920
|
// src/patcher.ts
|
|
13909
|
-
import { createHash as
|
|
13921
|
+
import { createHash as createHash9 } from "crypto";
|
|
13910
13922
|
import {
|
|
13911
13923
|
copyFileSync,
|
|
13912
13924
|
existsSync as existsSync7,
|
|
13913
13925
|
mkdtempSync,
|
|
13914
13926
|
mkdirSync as mkdirSync7,
|
|
13915
|
-
readFileSync as
|
|
13927
|
+
readFileSync as readFileSync10,
|
|
13916
13928
|
renameSync as renameSync3,
|
|
13917
13929
|
rmSync,
|
|
13918
|
-
statSync as
|
|
13930
|
+
statSync as statSync5,
|
|
13919
13931
|
unlinkSync as unlinkSync4,
|
|
13920
13932
|
writeFileSync as writeFileSync7,
|
|
13921
13933
|
openSync as openSync4,
|
|
@@ -13927,9 +13939,380 @@ import { basename, dirname as dirname5, join as join8 } from "path";
|
|
|
13927
13939
|
import pc12 from "picocolors";
|
|
13928
13940
|
import * as p11 from "@clack/prompts";
|
|
13929
13941
|
|
|
13930
|
-
// src/
|
|
13942
|
+
// src/local-patches.ts
|
|
13931
13943
|
import { createHash as createHash7 } from "crypto";
|
|
13932
|
-
import {
|
|
13944
|
+
import { readFileSync as readFileSync8, statSync as statSync3 } from "fs";
|
|
13945
|
+
var LOCAL_PATCH_API_VERSION = 1;
|
|
13946
|
+
var LOCAL_PATCH_ID_PATTERN = /^[a-z0-9][a-z0-9._-]{0,63}$/;
|
|
13947
|
+
var MAX_LOCAL_PATCH_BYTES = 1024 * 1024;
|
|
13948
|
+
var RESERVED_MARKER_PREFIX = "/*ccpatch:";
|
|
13949
|
+
var LOCAL_MARKER_PREFIX = "/*clodex-local:";
|
|
13950
|
+
var MAX_ERROR_LENGTH = 300;
|
|
13951
|
+
var localModuleEvaluationSequence = 0;
|
|
13952
|
+
function describeLocalError(error) {
|
|
13953
|
+
const raw = error instanceof Error ? `${error.name}: ${error.message}` : String(error);
|
|
13954
|
+
const redacted = raw.replace(
|
|
13955
|
+
/data:text\/javascript;base64,[^\s"'`]+/g,
|
|
13956
|
+
"<local-patches.mjs>"
|
|
13957
|
+
);
|
|
13958
|
+
const singleLine = redacted.replace(/[\r\n\t]+/g, " ").replace(/\s{2,}/g, " ").trim();
|
|
13959
|
+
if (singleLine.length <= MAX_ERROR_LENGTH) return singleLine || "unknown error";
|
|
13960
|
+
return `${singleLine.slice(0, MAX_ERROR_LENGTH - 1)}\u2026`;
|
|
13961
|
+
}
|
|
13962
|
+
function describeLocalId(id) {
|
|
13963
|
+
const encoded = JSON.stringify(id);
|
|
13964
|
+
return encoded.length <= 96 ? encoded : `${encoded.slice(0, 95)}\u2026`;
|
|
13965
|
+
}
|
|
13966
|
+
function countOccurrences(source, needle) {
|
|
13967
|
+
let count = 0;
|
|
13968
|
+
let offset = 0;
|
|
13969
|
+
while ((offset = source.indexOf(needle, offset)) !== -1) {
|
|
13970
|
+
count += 1;
|
|
13971
|
+
offset += needle.length;
|
|
13972
|
+
}
|
|
13973
|
+
return count;
|
|
13974
|
+
}
|
|
13975
|
+
function reservedMarkerInventory(source) {
|
|
13976
|
+
const count = countOccurrences(source, RESERVED_MARKER_PREFIX);
|
|
13977
|
+
const markers = source.match(/\/\*ccpatch:[\s\S]*?\*\//g)?.sort() ?? [];
|
|
13978
|
+
return JSON.stringify([count, markers]);
|
|
13979
|
+
}
|
|
13980
|
+
function localMarkerInventory(source) {
|
|
13981
|
+
const count = countOccurrences(source, LOCAL_MARKER_PREFIX);
|
|
13982
|
+
const markers = source.match(/\/\*clodex-local:[\s\S]*?\*\//g)?.sort() ?? [];
|
|
13983
|
+
return JSON.stringify([count, markers]);
|
|
13984
|
+
}
|
|
13985
|
+
function inspectLocalPatchSource(enabled) {
|
|
13986
|
+
const path = getLocalPatchesPath();
|
|
13987
|
+
if (!enabled) return { enabled: false, path };
|
|
13988
|
+
try {
|
|
13989
|
+
const stats = statSync3(path);
|
|
13990
|
+
if (!stats.isFile()) throw Object.assign(new Error("not a regular file"), { code: "EINVAL" });
|
|
13991
|
+
if (stats.size > MAX_LOCAL_PATCH_BYTES) {
|
|
13992
|
+
throw Object.assign(new Error("module exceeds 1 MiB"), { code: "EFBIG" });
|
|
13993
|
+
}
|
|
13994
|
+
const bytes = readFileSync8(path);
|
|
13995
|
+
if (bytes.length > MAX_LOCAL_PATCH_BYTES) {
|
|
13996
|
+
throw Object.assign(new Error("module exceeds 1 MiB"), { code: "EFBIG" });
|
|
13997
|
+
}
|
|
13998
|
+
const digest = createHash7("sha256").update(bytes).digest("hex");
|
|
13999
|
+
const source = bytes.toString("utf8");
|
|
14000
|
+
if (!Buffer.from(source, "utf8").equals(bytes)) {
|
|
14001
|
+
return {
|
|
14002
|
+
enabled: true,
|
|
14003
|
+
path,
|
|
14004
|
+
configIdentity: `v${LOCAL_PATCH_API_VERSION}:invalid-utf8:${digest}`,
|
|
14005
|
+
error: `could not read ${path}: module must be UTF-8`
|
|
14006
|
+
};
|
|
14007
|
+
}
|
|
14008
|
+
return {
|
|
14009
|
+
enabled: true,
|
|
14010
|
+
path,
|
|
14011
|
+
configIdentity: `v${LOCAL_PATCH_API_VERSION}:${digest}`,
|
|
14012
|
+
source
|
|
14013
|
+
};
|
|
14014
|
+
} catch (error) {
|
|
14015
|
+
const code = error instanceof Error && "code" in error ? String(error.code) : "UNKNOWN";
|
|
14016
|
+
const reason = code === "EFBIG" ? "module exceeds 1 MiB" : code === "EINVAL" ? "not a regular file" : code;
|
|
14017
|
+
return {
|
|
14018
|
+
enabled: true,
|
|
14019
|
+
path,
|
|
14020
|
+
configIdentity: `v${LOCAL_PATCH_API_VERSION}:unavailable:${code}`,
|
|
14021
|
+
error: `could not read ${path}: ${reason}`
|
|
14022
|
+
};
|
|
14023
|
+
}
|
|
14024
|
+
}
|
|
14025
|
+
function failedLocalTransaction(source, patches, results, failedIndex, extra) {
|
|
14026
|
+
const failedName = `LOCAL ${patches[failedIndex].id}`;
|
|
14027
|
+
const rolledBack = results.map((result) => result.status === "OK" ? {
|
|
14028
|
+
status: "SKIP",
|
|
14029
|
+
name: result.name,
|
|
14030
|
+
extra: `rolled back after ${failedName} failed`
|
|
14031
|
+
} : result);
|
|
14032
|
+
rolledBack.push({ status: "FAIL", name: failedName, extra });
|
|
14033
|
+
for (const remaining of patches.slice(failedIndex + 1)) {
|
|
14034
|
+
rolledBack.push({
|
|
14035
|
+
status: "SKIP",
|
|
14036
|
+
name: `LOCAL ${remaining.id}`,
|
|
14037
|
+
extra: `not run after ${failedName} failed`
|
|
14038
|
+
});
|
|
14039
|
+
}
|
|
14040
|
+
return { content: source, results: rolledBack };
|
|
14041
|
+
}
|
|
14042
|
+
function snapshotLocalPatchDefinitions(value) {
|
|
14043
|
+
if (!Array.isArray(value)) return "default export must be an array";
|
|
14044
|
+
const rawDefinitions = [...value];
|
|
14045
|
+
const snapshot = [];
|
|
14046
|
+
const ids = /* @__PURE__ */ new Set();
|
|
14047
|
+
for (const [index, definition] of rawDefinitions.entries()) {
|
|
14048
|
+
if (definition === null || typeof definition !== "object") {
|
|
14049
|
+
return `entry at index ${index} must be an object`;
|
|
14050
|
+
}
|
|
14051
|
+
const candidate = definition;
|
|
14052
|
+
const id = candidate.id;
|
|
14053
|
+
const apply = candidate.apply;
|
|
14054
|
+
if (typeof id !== "string") return `entry at index ${index} must have a string id`;
|
|
14055
|
+
if (typeof apply !== "function") {
|
|
14056
|
+
return `entry ${describeLocalId(id)} must have an apply function`;
|
|
14057
|
+
}
|
|
14058
|
+
if (!LOCAL_PATCH_ID_PATTERN.test(id)) {
|
|
14059
|
+
return `invalid id at index ${index}: ${describeLocalId(id)}`;
|
|
14060
|
+
}
|
|
14061
|
+
if (ids.has(id)) return `duplicate id at index ${index}: ${describeLocalId(id)}`;
|
|
14062
|
+
ids.add(id);
|
|
14063
|
+
snapshot.push(Object.freeze({ id, apply }));
|
|
14064
|
+
}
|
|
14065
|
+
return Object.freeze(snapshot);
|
|
14066
|
+
}
|
|
14067
|
+
function applyLocalPatchSnapshot(source, patches) {
|
|
14068
|
+
let content = source;
|
|
14069
|
+
const results = [];
|
|
14070
|
+
for (const [index, patch] of patches.entries()) {
|
|
14071
|
+
const marker = `/*clodex-local:${patch.id}*/`;
|
|
14072
|
+
const existingMarkerCount = countOccurrences(content, marker);
|
|
14073
|
+
if (existingMarkerCount > 1) {
|
|
14074
|
+
return failedLocalTransaction(
|
|
14075
|
+
source,
|
|
14076
|
+
patches,
|
|
14077
|
+
results,
|
|
14078
|
+
index,
|
|
14079
|
+
"existing marker appears more than once"
|
|
14080
|
+
);
|
|
14081
|
+
}
|
|
14082
|
+
if (existingMarkerCount === 1) {
|
|
14083
|
+
results.push({
|
|
14084
|
+
status: "SKIP",
|
|
14085
|
+
name: `LOCAL ${patch.id}`,
|
|
14086
|
+
extra: "already patched"
|
|
14087
|
+
});
|
|
14088
|
+
continue;
|
|
14089
|
+
}
|
|
14090
|
+
let next;
|
|
14091
|
+
const reservedMarkers = reservedMarkerInventory(content);
|
|
14092
|
+
const localMarkers = localMarkerInventory(content);
|
|
14093
|
+
try {
|
|
14094
|
+
next = patch.apply(content, { marker });
|
|
14095
|
+
} catch (error) {
|
|
14096
|
+
return failedLocalTransaction(
|
|
14097
|
+
source,
|
|
14098
|
+
patches,
|
|
14099
|
+
results,
|
|
14100
|
+
index,
|
|
14101
|
+
describeLocalError(error)
|
|
14102
|
+
);
|
|
14103
|
+
}
|
|
14104
|
+
if (typeof next !== "string") {
|
|
14105
|
+
return failedLocalTransaction(
|
|
14106
|
+
source,
|
|
14107
|
+
patches,
|
|
14108
|
+
results,
|
|
14109
|
+
index,
|
|
14110
|
+
`transform returned ${typeof next} instead of a string`
|
|
14111
|
+
);
|
|
14112
|
+
}
|
|
14113
|
+
if (reservedMarkerInventory(next) !== reservedMarkers) {
|
|
14114
|
+
return failedLocalTransaction(
|
|
14115
|
+
source,
|
|
14116
|
+
patches,
|
|
14117
|
+
results,
|
|
14118
|
+
index,
|
|
14119
|
+
"transform changed reserved /*ccpatch: markers"
|
|
14120
|
+
);
|
|
14121
|
+
}
|
|
14122
|
+
const markerCount = countOccurrences(next, marker);
|
|
14123
|
+
if (markerCount !== 1) {
|
|
14124
|
+
return failedLocalTransaction(
|
|
14125
|
+
source,
|
|
14126
|
+
patches,
|
|
14127
|
+
results,
|
|
14128
|
+
index,
|
|
14129
|
+
markerCount === 0 ? `transform did not emit ${marker}` : `transform must emit exactly one ${marker} marker`
|
|
14130
|
+
);
|
|
14131
|
+
}
|
|
14132
|
+
if (localMarkerInventory(next.replace(marker, "")) !== localMarkers) {
|
|
14133
|
+
return failedLocalTransaction(
|
|
14134
|
+
source,
|
|
14135
|
+
patches,
|
|
14136
|
+
results,
|
|
14137
|
+
index,
|
|
14138
|
+
"transform changed another local patch marker"
|
|
14139
|
+
);
|
|
14140
|
+
}
|
|
14141
|
+
content = next;
|
|
14142
|
+
results.push({ status: "OK", name: `LOCAL ${patch.id}` });
|
|
14143
|
+
}
|
|
14144
|
+
return { content, results };
|
|
14145
|
+
}
|
|
14146
|
+
function localPatchSetFailure(source, extra) {
|
|
14147
|
+
return {
|
|
14148
|
+
content: source,
|
|
14149
|
+
results: [{ status: "FAIL", name: "LOCAL PATCH SET", extra }]
|
|
14150
|
+
};
|
|
14151
|
+
}
|
|
14152
|
+
async function applyLocalPatches(source, localSource) {
|
|
14153
|
+
if (!localSource.enabled) return { content: source, results: [] };
|
|
14154
|
+
if (typeof localSource.error === "string") {
|
|
14155
|
+
return localPatchSetFailure(source, localSource.error);
|
|
14156
|
+
}
|
|
14157
|
+
try {
|
|
14158
|
+
const moduleUrl = `data:text/javascript;base64,${Buffer.from(localSource.source).toString("base64")}#clodex-local-eval-${++localModuleEvaluationSequence}`;
|
|
14159
|
+
const loaded = await import(
|
|
14160
|
+
/* @vite-ignore */
|
|
14161
|
+
moduleUrl
|
|
14162
|
+
);
|
|
14163
|
+
const definitions = snapshotLocalPatchDefinitions(loaded.default);
|
|
14164
|
+
if (typeof definitions === "string") {
|
|
14165
|
+
return localPatchSetFailure(source, definitions);
|
|
14166
|
+
}
|
|
14167
|
+
if (definitions.length === 0) {
|
|
14168
|
+
return {
|
|
14169
|
+
content: source,
|
|
14170
|
+
results: [{
|
|
14171
|
+
status: "SKIP",
|
|
14172
|
+
name: "LOCAL PATCH SET",
|
|
14173
|
+
extra: "no patches exported"
|
|
14174
|
+
}]
|
|
14175
|
+
};
|
|
14176
|
+
}
|
|
14177
|
+
return applyLocalPatchSnapshot(source, definitions);
|
|
14178
|
+
} catch (error) {
|
|
14179
|
+
return localPatchSetFailure(
|
|
14180
|
+
source,
|
|
14181
|
+
describeLocalError(error)
|
|
14182
|
+
);
|
|
14183
|
+
}
|
|
14184
|
+
}
|
|
14185
|
+
|
|
14186
|
+
// src/built-in-patch-proofs.ts
|
|
14187
|
+
function escapeRegex(value) {
|
|
14188
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
14189
|
+
}
|
|
14190
|
+
function countExactOccurrences(source, needle) {
|
|
14191
|
+
let count = 0;
|
|
14192
|
+
let offset = 0;
|
|
14193
|
+
while ((offset = source.indexOf(needle, offset)) !== -1) {
|
|
14194
|
+
count += 1;
|
|
14195
|
+
offset += needle.length;
|
|
14196
|
+
}
|
|
14197
|
+
return count;
|
|
14198
|
+
}
|
|
14199
|
+
function uniqueMatch(source, name, pattern) {
|
|
14200
|
+
const flags = pattern.flags.includes("g") ? pattern.flags : `${pattern.flags}g`;
|
|
14201
|
+
const matcher = new RegExp(pattern.source, flags);
|
|
14202
|
+
const match = matcher.exec(source);
|
|
14203
|
+
if (!match || matcher.exec(source)) {
|
|
14204
|
+
throw new Error(`clodex patch: could not capture built-in postcondition: ${name}`);
|
|
14205
|
+
}
|
|
14206
|
+
return match[0];
|
|
14207
|
+
}
|
|
14208
|
+
function captureNeedle(source, name, needle) {
|
|
14209
|
+
if (countExactOccurrences(source, needle) !== 1) {
|
|
14210
|
+
throw new Error(`clodex patch: could not capture built-in postcondition: ${name}`);
|
|
14211
|
+
}
|
|
14212
|
+
return {
|
|
14213
|
+
name,
|
|
14214
|
+
protectedText: needle,
|
|
14215
|
+
occurrences: 1
|
|
14216
|
+
};
|
|
14217
|
+
}
|
|
14218
|
+
function capturePattern(source, name, pattern) {
|
|
14219
|
+
return captureNeedle(source, name, uniqueMatch(source, name, pattern));
|
|
14220
|
+
}
|
|
14221
|
+
function normalizedPatchName(name) {
|
|
14222
|
+
return name.replace(/ \(refresh\)$/, "");
|
|
14223
|
+
}
|
|
14224
|
+
function protectsResult(results, name) {
|
|
14225
|
+
const result = results.find((entry) => normalizedPatchName(entry.name) === name);
|
|
14226
|
+
return result !== void 0 && result.status !== "FAIL" && result.extra !== "no aliases configured";
|
|
14227
|
+
}
|
|
14228
|
+
function configuredAliases(config) {
|
|
14229
|
+
const aliases = /* @__PURE__ */ new Map();
|
|
14230
|
+
for (const [id, entry] of Object.entries(config)) {
|
|
14231
|
+
if (entry.alias === void 0) continue;
|
|
14232
|
+
const alias = String(entry.alias).trim().toLowerCase();
|
|
14233
|
+
aliases.set(alias, {
|
|
14234
|
+
id,
|
|
14235
|
+
...entry.display === void 0 ? {} : { display: String(entry.display) }
|
|
14236
|
+
});
|
|
14237
|
+
}
|
|
14238
|
+
return [...aliases].map(([alias, entry]) => ({ alias, ...entry }));
|
|
14239
|
+
}
|
|
14240
|
+
function effortProofPattern(marker) {
|
|
14241
|
+
return new RegExp(
|
|
14242
|
+
escapeRegex(marker) + 'var _ccv=Object\\.assign\\(Object\\.create\\(null\\),\\{[^{}]*\\}\\)\\[String\\([\\w$]+\\|\\|""\\)\\.trim\\(\\)\\.toLowerCase\\(\\)\\];if\\(_ccv!==void 0\\)return _ccv;'
|
|
14243
|
+
);
|
|
14244
|
+
}
|
|
14245
|
+
function captureBuiltInPatchProofs(source, config, results) {
|
|
14246
|
+
const proofs = [];
|
|
14247
|
+
const addPattern = (name, pattern) => {
|
|
14248
|
+
if (protectsResult(results, name)) {
|
|
14249
|
+
proofs.push(capturePattern(source, name, pattern));
|
|
14250
|
+
}
|
|
14251
|
+
};
|
|
14252
|
+
addPattern(
|
|
14253
|
+
"PATCH 1: Agent tool model enum",
|
|
14254
|
+
/\.enum\(\["sonnet","opus","haiku"(?:,"[^"]+")*\]\)\.optional\(\)\.describe\(/
|
|
14255
|
+
);
|
|
14256
|
+
addPattern(
|
|
14257
|
+
"PATCH 3: known-alias validator list",
|
|
14258
|
+
/\["sonnet","opus","haiku","fable"(?:,"[^"]+")*,"opusplan"(?:,"[^"]+")*\]/
|
|
14259
|
+
);
|
|
14260
|
+
addPattern(
|
|
14261
|
+
"PATCH 4: Agent tool model description",
|
|
14262
|
+
/describe\(`Optional model override for this agent[^`]*?`\)/
|
|
14263
|
+
);
|
|
14264
|
+
const aliases = configuredAliases(config);
|
|
14265
|
+
if (protectsResult(results, "PATCH 6: alias resolver switch")) {
|
|
14266
|
+
for (const { alias } of aliases) {
|
|
14267
|
+
const value = JSON.stringify(alias);
|
|
14268
|
+
proofs.push(captureNeedle(
|
|
14269
|
+
source,
|
|
14270
|
+
`PATCH 6: alias resolver switch (${alias})`,
|
|
14271
|
+
`case${value}:return ${value};`
|
|
14272
|
+
));
|
|
14273
|
+
}
|
|
14274
|
+
}
|
|
14275
|
+
if (protectsResult(results, "PATCH 5: model picker options")) {
|
|
14276
|
+
for (const { alias, id, display } of aliases) {
|
|
14277
|
+
const entry = "{value:" + JSON.stringify(alias) + ",label:" + JSON.stringify(alias.charAt(0).toUpperCase() + alias.slice(1)) + ",description:" + JSON.stringify(display ?? `Custom model (${id})`) + "}";
|
|
14278
|
+
proofs.push(captureNeedle(
|
|
14279
|
+
source,
|
|
14280
|
+
`PATCH 5: model picker options (${alias})`,
|
|
14281
|
+
entry
|
|
14282
|
+
));
|
|
14283
|
+
}
|
|
14284
|
+
}
|
|
14285
|
+
addPattern(
|
|
14286
|
+
"PATCH 7: per-model context window",
|
|
14287
|
+
/\/\*ccpatch:ctx\*\/var _ccw=\(\{[^{}]*\}\)\[[^\]]*\];if\(_ccw!==void 0\)return _ccw;/
|
|
14288
|
+
);
|
|
14289
|
+
addPattern(
|
|
14290
|
+
"PATCH 8a: effort capability",
|
|
14291
|
+
effortProofPattern("/*ccpatch:effort*/")
|
|
14292
|
+
);
|
|
14293
|
+
addPattern(
|
|
14294
|
+
"PATCH 8b: xhigh effort capability",
|
|
14295
|
+
effortProofPattern("/*ccpatch:xhigh-effort*/")
|
|
14296
|
+
);
|
|
14297
|
+
addPattern(
|
|
14298
|
+
"PATCH 8c: max effort capability",
|
|
14299
|
+
effortProofPattern("/*ccpatch:max-effort*/")
|
|
14300
|
+
);
|
|
14301
|
+
addPattern(
|
|
14302
|
+
"PATCH 9: default effort",
|
|
14303
|
+
/\/\*ccpatch:default-effort\*\/var _cce=Object\.assign\(Object\.create\(null\),\{[^{}]*\}\)\[String\([\w$]+\|\|""\)\.trim\(\)\.toLowerCase\(\)\];if\(_cce!==void 0\)return _cce;/
|
|
14304
|
+
);
|
|
14305
|
+
return proofs;
|
|
14306
|
+
}
|
|
14307
|
+
function builtInPatchProofsChanged(source, proofs) {
|
|
14308
|
+
return proofs.some(
|
|
14309
|
+
(proof) => countExactOccurrences(source, proof.protectedText) !== proof.occurrences
|
|
14310
|
+
);
|
|
14311
|
+
}
|
|
14312
|
+
|
|
14313
|
+
// src/patch-backup.ts
|
|
14314
|
+
import { createHash as createHash8 } from "crypto";
|
|
14315
|
+
import { existsSync as existsSync6, readFileSync as readFileSync9, readdirSync, statSync as statSync4 } from "fs";
|
|
13933
14316
|
import { homedir as homedir2 } from "os";
|
|
13934
14317
|
import { join as join7 } from "path";
|
|
13935
14318
|
var BACKUP_SHA_PREFIX_LENGTH = 16;
|
|
@@ -13937,7 +14320,7 @@ function backupDir() {
|
|
|
13937
14320
|
return process.env["TWEAKCC_CONFIG_DIR"]?.trim() || join7(homedir2(), ".tweakcc");
|
|
13938
14321
|
}
|
|
13939
14322
|
function sha256File(path) {
|
|
13940
|
-
return
|
|
14323
|
+
return createHash8("sha256").update(readFileSync9(path)).digest("hex");
|
|
13941
14324
|
}
|
|
13942
14325
|
function backupVersionTag(version) {
|
|
13943
14326
|
const tag = version.trim().replace(/[^\w.-]+/g, "_");
|
|
@@ -13967,7 +14350,7 @@ function scanPristineBackups(version, dir = backupDir()) {
|
|
|
13967
14350
|
const path = join7(dir, entry);
|
|
13968
14351
|
let sha256;
|
|
13969
14352
|
try {
|
|
13970
|
-
if (!
|
|
14353
|
+
if (!statSync4(path).isFile()) continue;
|
|
13971
14354
|
sha256 = sha256File(path);
|
|
13972
14355
|
} catch {
|
|
13973
14356
|
corrupt.push(path);
|
|
@@ -14388,7 +14771,7 @@ function getPatchLockPath() {
|
|
|
14388
14771
|
}
|
|
14389
14772
|
function readPatchManifest(path = getPatchManifestPath()) {
|
|
14390
14773
|
try {
|
|
14391
|
-
const parsed = JSON.parse(
|
|
14774
|
+
const parsed = JSON.parse(readFileSync10(path, "utf8"));
|
|
14392
14775
|
if (parsed && typeof parsed.binaryPath === "string" && typeof parsed.configHash === "string") {
|
|
14393
14776
|
return parsed;
|
|
14394
14777
|
}
|
|
@@ -14454,7 +14837,7 @@ function reportRejectedModelAliases(rejections) {
|
|
|
14454
14837
|
);
|
|
14455
14838
|
}
|
|
14456
14839
|
}
|
|
14457
|
-
function computePatchConfigHash(config, transformsVersion = PATCH_TRANSFORMS_VERSION) {
|
|
14840
|
+
function computePatchConfigHash(config, transformsVersion = PATCH_TRANSFORMS_VERSION, localPatchIdentity) {
|
|
14458
14841
|
const canonical = Object.keys(config).sort().map((key) => {
|
|
14459
14842
|
const entry = config[key];
|
|
14460
14843
|
return [
|
|
@@ -14466,7 +14849,11 @@ function computePatchConfigHash(config, transformsVersion = PATCH_TRANSFORMS_VER
|
|
|
14466
14849
|
entry.effort?.defaultLevel ?? null
|
|
14467
14850
|
];
|
|
14468
14851
|
});
|
|
14469
|
-
|
|
14852
|
+
const payload = [transformsVersion, canonical];
|
|
14853
|
+
if (localPatchIdentity !== void 0) {
|
|
14854
|
+
payload.push(["local-patches", localPatchIdentity]);
|
|
14855
|
+
}
|
|
14856
|
+
return createHash9("sha256").update(JSON.stringify(payload)).digest("hex");
|
|
14470
14857
|
}
|
|
14471
14858
|
function buildDesiredPatchConfig() {
|
|
14472
14859
|
const prefs = loadPreferences();
|
|
@@ -14537,7 +14924,7 @@ function tryAcquirePatchLock(lockPath = getPatchLockPath(), opts = {}) {
|
|
|
14537
14924
|
} catch {
|
|
14538
14925
|
let stale = false;
|
|
14539
14926
|
try {
|
|
14540
|
-
const existing = JSON.parse(
|
|
14927
|
+
const existing = JSON.parse(readFileSync10(lockPath, "utf8"));
|
|
14541
14928
|
stale = !existing.pid || !isAlive(existing.pid) || typeof existing.startedAt === "number" && now - existing.startedAt > PATCH_LOCK_STALE_MS;
|
|
14542
14929
|
} catch {
|
|
14543
14930
|
stale = true;
|
|
@@ -14563,7 +14950,7 @@ function resolveClaudeBinaryForPatch() {
|
|
|
14563
14950
|
return { ok: false, reason: "binary-not-found" };
|
|
14564
14951
|
}
|
|
14565
14952
|
try {
|
|
14566
|
-
if (!
|
|
14953
|
+
if (!statSync5(resolved).isFile()) return { ok: false, reason: "binary-not-found" };
|
|
14567
14954
|
} catch {
|
|
14568
14955
|
return { ok: false, reason: "binary-not-found" };
|
|
14569
14956
|
}
|
|
@@ -14615,6 +15002,49 @@ function requiredEffortPatchFailures(results) {
|
|
|
14615
15002
|
(result) => result.status === "FAIL" && (result.name.startsWith("PATCH 8a:") || result.name.startsWith("PATCH 8b:") || result.name.startsWith("PATCH 8c:") || result.name.startsWith("PATCH 9:"))
|
|
14616
15003
|
);
|
|
14617
15004
|
}
|
|
15005
|
+
function builtInPatchVerificationFailed(source, config, expected, proofs) {
|
|
15006
|
+
if (builtInPatchProofsChanged(source, proofs)) return true;
|
|
15007
|
+
let verification;
|
|
15008
|
+
try {
|
|
15009
|
+
verification = applyClodexPatches(source, config);
|
|
15010
|
+
} catch {
|
|
15011
|
+
return true;
|
|
15012
|
+
}
|
|
15013
|
+
if (verification.content !== source || verification.results.length !== expected.length) {
|
|
15014
|
+
return true;
|
|
15015
|
+
}
|
|
15016
|
+
const expectedByName = new Map(expected.map((result) => [
|
|
15017
|
+
result.name.replace(/ \(refresh\)$/, ""),
|
|
15018
|
+
result
|
|
15019
|
+
]));
|
|
15020
|
+
for (const result of verification.results) {
|
|
15021
|
+
const original = expectedByName.get(result.name.replace(/ \(refresh\)$/, ""));
|
|
15022
|
+
if (!original) return true;
|
|
15023
|
+
if (original.status === "FAIL") {
|
|
15024
|
+
if (result.status !== "FAIL" || result.extra !== original.extra) return true;
|
|
15025
|
+
} else if (result.status !== "SKIP") {
|
|
15026
|
+
return true;
|
|
15027
|
+
}
|
|
15028
|
+
}
|
|
15029
|
+
return false;
|
|
15030
|
+
}
|
|
15031
|
+
function discardLocalPatchOutcome(builtInContent, results) {
|
|
15032
|
+
return {
|
|
15033
|
+
content: builtInContent,
|
|
15034
|
+
results: [
|
|
15035
|
+
...results.map((result) => result.status === "OK" ? {
|
|
15036
|
+
status: "SKIP",
|
|
15037
|
+
name: result.name,
|
|
15038
|
+
extra: "rolled back after built-in verification failed"
|
|
15039
|
+
} : result),
|
|
15040
|
+
{
|
|
15041
|
+
status: "FAIL",
|
|
15042
|
+
name: "LOCAL PATCH SET",
|
|
15043
|
+
extra: "local output changed built-in patch sites"
|
|
15044
|
+
}
|
|
15045
|
+
]
|
|
15046
|
+
};
|
|
15047
|
+
}
|
|
14618
15048
|
async function applyPatch(binaryPath, version, desired, configHash, opts) {
|
|
14619
15049
|
let candidateDir;
|
|
14620
15050
|
let results;
|
|
@@ -14678,8 +15108,8 @@ async function applyPatch(binaryPath, version, desired, configHash, opts) {
|
|
|
14678
15108
|
backup = canonical;
|
|
14679
15109
|
}
|
|
14680
15110
|
publishBackupFile(backup, tweakccMirrorBackupPath());
|
|
14681
|
-
const
|
|
14682
|
-
results =
|
|
15111
|
+
const builtIn = applyClodexPatches(loaded.source, desired.config);
|
|
15112
|
+
results = builtIn.results;
|
|
14683
15113
|
const failedEffortPatches = requiredEffortPatchFailures(results);
|
|
14684
15114
|
if (failedEffortPatches.length > 0) {
|
|
14685
15115
|
throw new PatchApplyError(
|
|
@@ -14687,8 +15117,38 @@ async function applyPatch(binaryPath, version, desired, configHash, opts) {
|
|
|
14687
15117
|
results
|
|
14688
15118
|
);
|
|
14689
15119
|
}
|
|
14690
|
-
|
|
14691
|
-
|
|
15120
|
+
let builtInProofs = [];
|
|
15121
|
+
let local;
|
|
15122
|
+
if (opts.localPatches?.enabled && typeof opts.localPatches.source === "string") {
|
|
15123
|
+
try {
|
|
15124
|
+
builtInProofs = captureBuiltInPatchProofs(
|
|
15125
|
+
builtIn.content,
|
|
15126
|
+
desired.config,
|
|
15127
|
+
builtIn.results
|
|
15128
|
+
);
|
|
15129
|
+
} catch {
|
|
15130
|
+
local = {
|
|
15131
|
+
content: builtIn.content,
|
|
15132
|
+
results: [{
|
|
15133
|
+
status: "FAIL",
|
|
15134
|
+
name: "LOCAL PATCH SET",
|
|
15135
|
+
extra: "could not capture built-in postconditions"
|
|
15136
|
+
}]
|
|
15137
|
+
};
|
|
15138
|
+
}
|
|
15139
|
+
}
|
|
15140
|
+
local ??= opts.localPatches ? await applyLocalPatches(builtIn.content, opts.localPatches) : { content: builtIn.content, results: [] };
|
|
15141
|
+
if (local.results.some((result) => result.status === "OK") && builtInPatchVerificationFailed(
|
|
15142
|
+
local.content,
|
|
15143
|
+
desired.config,
|
|
15144
|
+
builtIn.results,
|
|
15145
|
+
builtInProofs
|
|
15146
|
+
)) {
|
|
15147
|
+
local = discardLocalPatchOutcome(builtIn.content, local.results);
|
|
15148
|
+
}
|
|
15149
|
+
results = [...results, ...local.results];
|
|
15150
|
+
await writeContent(loaded.installation, local.content);
|
|
15151
|
+
patchedSize = statSync5(candidatePath).size;
|
|
14692
15152
|
patchedSha256 = sha256File(candidatePath);
|
|
14693
15153
|
renameSync3(candidatePath, binaryPath);
|
|
14694
15154
|
} catch (err) {
|
|
@@ -14788,6 +15248,9 @@ async function runPatchCommand(opts = {}) {
|
|
|
14788
15248
|
return 1;
|
|
14789
15249
|
}
|
|
14790
15250
|
const { binaryPath, version } = target;
|
|
15251
|
+
if (opts.localPatches !== void 0) {
|
|
15252
|
+
savePreferences({ localPatchesEnabled: opts.localPatches });
|
|
15253
|
+
}
|
|
14791
15254
|
const desired = buildDesiredPatchConfig();
|
|
14792
15255
|
if (Object.keys(desired.config).length === 0) {
|
|
14793
15256
|
p11.log.error("No favorite models to patch. Save favorites with `clodex models` first.");
|
|
@@ -14797,13 +15260,20 @@ async function runPatchCommand(opts = {}) {
|
|
|
14797
15260
|
p11.log.warn(`No context window metadata for ${id} \u2014 Claude Code will assume the 200k default.`);
|
|
14798
15261
|
}
|
|
14799
15262
|
reportRejectedModelAliases(desired.rejectedAliasRejections);
|
|
14800
|
-
const
|
|
15263
|
+
const localPatches = inspectLocalPatchSource(
|
|
15264
|
+
loadPreferences().localPatchesEnabled === true
|
|
15265
|
+
);
|
|
15266
|
+
const configHash = computePatchConfigHash(
|
|
15267
|
+
desired.config,
|
|
15268
|
+
PATCH_TRANSFORMS_VERSION,
|
|
15269
|
+
localPatches.enabled ? localPatches.configIdentity : void 0
|
|
15270
|
+
);
|
|
14801
15271
|
const manifest = readPatchManifest();
|
|
14802
15272
|
const state = evaluatePatchState(manifest, {
|
|
14803
15273
|
binaryPath,
|
|
14804
15274
|
claudeVersion: version,
|
|
14805
15275
|
configHash,
|
|
14806
|
-
binarySize:
|
|
15276
|
+
binarySize: statSync5(binaryPath).size
|
|
14807
15277
|
});
|
|
14808
15278
|
if (state === "current") {
|
|
14809
15279
|
p11.log.success(`claude ${version} is already patched with the current model config \u2014 nothing to do.`);
|
|
@@ -14817,7 +15287,8 @@ async function runPatchCommand(opts = {}) {
|
|
|
14817
15287
|
try {
|
|
14818
15288
|
const outcome = await applyPatch(binaryPath, version, desired, configHash, {
|
|
14819
15289
|
trace: opts.trace ?? false,
|
|
14820
|
-
manifest
|
|
15290
|
+
manifest,
|
|
15291
|
+
localPatches
|
|
14821
15292
|
});
|
|
14822
15293
|
if (!outcome.ok) {
|
|
14823
15294
|
p11.log.error(outcome.message);
|
|
@@ -14845,13 +15316,20 @@ async function runLaunchPatchCheck(opts = {}) {
|
|
|
14845
15316
|
return;
|
|
14846
15317
|
}
|
|
14847
15318
|
const resolved = target;
|
|
14848
|
-
const
|
|
15319
|
+
const localPatches = inspectLocalPatchSource(
|
|
15320
|
+
loadPreferences().localPatchesEnabled === true
|
|
15321
|
+
);
|
|
15322
|
+
const configHash = computePatchConfigHash(
|
|
15323
|
+
desired.config,
|
|
15324
|
+
PATCH_TRANSFORMS_VERSION,
|
|
15325
|
+
localPatches.enabled ? localPatches.configIdentity : void 0
|
|
15326
|
+
);
|
|
14849
15327
|
const manifest = readPatchManifest();
|
|
14850
15328
|
const state = evaluatePatchState(manifest, {
|
|
14851
15329
|
binaryPath: resolved.binaryPath,
|
|
14852
15330
|
claudeVersion: resolved.version,
|
|
14853
15331
|
configHash,
|
|
14854
|
-
binarySize:
|
|
15332
|
+
binarySize: statSync5(resolved.binaryPath).size
|
|
14855
15333
|
});
|
|
14856
15334
|
if (state === "current") return;
|
|
14857
15335
|
const interactive = !opts.dryRun && !opts.agentStdout && process.stdin.isTTY === true && process.stdout.isTTY === true;
|
|
@@ -15057,9 +15535,22 @@ function parseArgs(args) {
|
|
|
15057
15535
|
if (arg === "--help" || arg === "-h") parsed2.showHelp = true;
|
|
15058
15536
|
else if (arg === "--version" || arg === "-v") parsed2.showVersion = true;
|
|
15059
15537
|
else if (arg === "--restore") parsed2.patchRestore = true;
|
|
15060
|
-
else if (arg === "--
|
|
15538
|
+
else if (arg === "--enable-local-patches") {
|
|
15539
|
+
if (parsed2.patchLocalPatches === false && !parsed2.error) {
|
|
15540
|
+
parsed2.error = "--enable-local-patches and --disable-local-patches cannot be combined";
|
|
15541
|
+
}
|
|
15542
|
+
parsed2.patchLocalPatches = true;
|
|
15543
|
+
} else if (arg === "--disable-local-patches") {
|
|
15544
|
+
if (parsed2.patchLocalPatches === true && !parsed2.error) {
|
|
15545
|
+
parsed2.error = "--enable-local-patches and --disable-local-patches cannot be combined";
|
|
15546
|
+
}
|
|
15547
|
+
parsed2.patchLocalPatches = false;
|
|
15548
|
+
} else if (arg === "--trace") parsed2.trace = true;
|
|
15061
15549
|
else if (!parsed2.error) parsed2.error = `Unknown patch option: ${arg}`;
|
|
15062
15550
|
}
|
|
15551
|
+
if (parsed2.patchRestore && parsed2.patchLocalPatches !== void 0 && !parsed2.error) {
|
|
15552
|
+
parsed2.error = "--restore cannot be combined with local-patch settings";
|
|
15553
|
+
}
|
|
15063
15554
|
return parsed2;
|
|
15064
15555
|
}
|
|
15065
15556
|
if (first !== "claude") {
|
|
@@ -15102,7 +15593,7 @@ Bridge Claude Code to OpenAI models \u2014 OpenAI API key or ChatGPT/Codex-plan
|
|
|
15102
15593
|
${pc13.bold("Usage:")}
|
|
15103
15594
|
clodex claude [options] [claude-flags]
|
|
15104
15595
|
clodex server [options]
|
|
15105
|
-
clodex patch [
|
|
15596
|
+
clodex patch [options]
|
|
15106
15597
|
clodex models
|
|
15107
15598
|
clodex favorites
|
|
15108
15599
|
clodex providers
|
|
@@ -15314,11 +15805,15 @@ real ids, and reporting the correct context window.
|
|
|
15314
15805
|
${pc13.bold("Usage:")}
|
|
15315
15806
|
clodex patch
|
|
15316
15807
|
clodex patch --restore
|
|
15808
|
+
clodex patch --enable-local-patches
|
|
15809
|
+
clodex patch --disable-local-patches
|
|
15317
15810
|
clodex patch --help
|
|
15318
15811
|
|
|
15319
15812
|
${pc13.bold("Options:")}
|
|
15320
|
-
--restore
|
|
15321
|
-
--trace
|
|
15813
|
+
--restore Restore the pristine Claude Code binary
|
|
15814
|
+
--trace Show per-patch-site results (OK/SKIP/FAIL)
|
|
15815
|
+
--enable-local-patches Persistently enable ~/.clodex/local-patches.mjs
|
|
15816
|
+
--disable-local-patches Disable local patches and rebuild without them
|
|
15322
15817
|
|
|
15323
15818
|
${pc13.bold("Behavior:")}
|
|
15324
15819
|
The patch map is built automatically from your clodex favorites and aliases
|
|
@@ -15326,6 +15821,10 @@ ${pc13.bold("Behavior:")}
|
|
|
15326
15821
|
per-version backup is kept, and a manifest (~/.clodex/patch-state.json)
|
|
15327
15822
|
makes re-runs no-ops until your config or Claude Code version changes \u2014
|
|
15328
15823
|
then the binary is restored first and re-patched fresh.
|
|
15824
|
+
|
|
15825
|
+
Local patches execute after the built-ins as an all-or-none set. Enabling
|
|
15826
|
+
them executes trusted JavaScript from local-patches.mjs with your full user
|
|
15827
|
+
permissions. Local failures are reported but never block the built-ins.
|
|
15329
15828
|
Run clodex patch again after every claude update.`;
|
|
15330
15829
|
}
|
|
15331
15830
|
function printHelp(text4) {
|
|
@@ -16132,7 +16631,11 @@ Error: ${parsed.error}
|
|
|
16132
16631
|
printHelp(patchHelpText());
|
|
16133
16632
|
return 0;
|
|
16134
16633
|
}
|
|
16135
|
-
return runPatchCommand({
|
|
16634
|
+
return runPatchCommand({
|
|
16635
|
+
restore: parsed.patchRestore,
|
|
16636
|
+
trace: parsed.trace,
|
|
16637
|
+
localPatches: parsed.patchLocalPatches
|
|
16638
|
+
});
|
|
16136
16639
|
}
|
|
16137
16640
|
if (parsed.showVersion) {
|
|
16138
16641
|
console.log(VERSION);
|