@bman654/clodex 2.14.0 → 2.14.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/dist/cli.js +95 -26
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -344,7 +344,7 @@ import { join } from "path";
|
|
|
344
344
|
// package.json
|
|
345
345
|
var package_default = {
|
|
346
346
|
name: "@bman654/clodex",
|
|
347
|
-
version: "2.14.
|
|
347
|
+
version: "2.14.1",
|
|
348
348
|
publishConfig: {
|
|
349
349
|
access: "public"
|
|
350
350
|
},
|
|
@@ -3432,7 +3432,7 @@ function savedStopsAfter(current, assignments) {
|
|
|
3432
3432
|
}
|
|
3433
3433
|
|
|
3434
3434
|
// src/patch-transforms.ts
|
|
3435
|
-
var PATCH_TRANSFORMS_VERSION =
|
|
3435
|
+
var PATCH_TRANSFORMS_VERSION = 13;
|
|
3436
3436
|
var NATIVE_EFFORT_LEVELS = ["low", "medium", "high", "xhigh", "max"];
|
|
3437
3437
|
var BASE_EFFORT_LEVELS = ["low", "medium", "high"];
|
|
3438
3438
|
function projectNativeEffort(effort) {
|
|
@@ -3589,14 +3589,22 @@ function applyClodexPatches(source, config) {
|
|
|
3589
3589
|
{ required: true, noopIsSkip: true }
|
|
3590
3590
|
);
|
|
3591
3591
|
{
|
|
3592
|
-
const
|
|
3592
|
+
const RESOLVER_ANCHOR = /(case"best":\{[^{}]*\})/;
|
|
3593
|
+
const RESOLVER_BUDGET = 2e3 + ALIASES.reduce((n, a) => n + 2 * a.length + 17, 0);
|
|
3594
|
+
const RESOLVER_SWITCH = new RegExp(
|
|
3595
|
+
'case"best":\\{[^{}]*\\}[\\s\\S]{0,' + RESOLVER_BUDGET + "}?default:return"
|
|
3596
|
+
);
|
|
3597
|
+
const resolver = js.match(RESOLVER_SWITCH)?.[0] ?? "";
|
|
3598
|
+
const missing = ALIASES.filter(
|
|
3599
|
+
(a) => !new RegExp("case" + reEsc(q(a)) + ":return").test(resolver)
|
|
3600
|
+
);
|
|
3593
3601
|
const cases = missing.map((a) => "case" + q(a) + ":return " + q(a) + ";").join("");
|
|
3594
3602
|
if (ALIASES.length === 0) {
|
|
3595
3603
|
log12("SKIP", "PATCH 6: alias resolver switch", "no aliases configured");
|
|
3596
3604
|
} else {
|
|
3597
3605
|
applyOnce(
|
|
3598
3606
|
"PATCH 6: alias resolver switch",
|
|
3599
|
-
|
|
3607
|
+
RESOLVER_ANCHOR,
|
|
3600
3608
|
(m) => m + cases,
|
|
3601
3609
|
{ required: true, noopIsSkip: true }
|
|
3602
3610
|
);
|
|
@@ -11641,6 +11649,7 @@ function routeUnavailableMessage(modelId, reason) {
|
|
|
11641
11649
|
}
|
|
11642
11650
|
|
|
11643
11651
|
// src/upstream-forward.ts
|
|
11652
|
+
import { randomUUID as randomUUID4 } from "crypto";
|
|
11644
11653
|
import { Readable, Transform } from "stream";
|
|
11645
11654
|
import { StringDecoder } from "string_decoder";
|
|
11646
11655
|
|
|
@@ -11729,18 +11738,50 @@ async function fetchWithOAuthRetry(apiKey, request3, refreshToken) {
|
|
|
11729
11738
|
response = await request3(refreshed);
|
|
11730
11739
|
return { response, apiKey: refreshed, refreshed: true };
|
|
11731
11740
|
}
|
|
11741
|
+
function upstreamHoldsThreads(url) {
|
|
11742
|
+
try {
|
|
11743
|
+
return new URL(url).hostname.toLowerCase() === "api.anthropic.com";
|
|
11744
|
+
} catch {
|
|
11745
|
+
return false;
|
|
11746
|
+
}
|
|
11747
|
+
}
|
|
11748
|
+
function anchorSafeMessageId(id) {
|
|
11749
|
+
return typeof id === "string" && id.startsWith("msg_") ? "clodex_" + randomUUID4().replace(/-/g, "") : id;
|
|
11750
|
+
}
|
|
11751
|
+
function isThreadContinuation(body) {
|
|
11752
|
+
const thread = body.thread;
|
|
11753
|
+
return typeof thread === "object" && thread !== null && thread.type === "continue";
|
|
11754
|
+
}
|
|
11755
|
+
var THREAD_UNSUPPORTED_BODY = JSON.stringify({
|
|
11756
|
+
type: "error",
|
|
11757
|
+
error: {
|
|
11758
|
+
type: "invalid_request_error",
|
|
11759
|
+
message: "thread continuation is not supported on this route; resend the full conversation",
|
|
11760
|
+
details: { error_code: "thread_unsupported_request" }
|
|
11761
|
+
}
|
|
11762
|
+
});
|
|
11732
11763
|
var SSE_LINE_SPLIT = /(\r\n|\r|\n)/;
|
|
11733
|
-
function
|
|
11764
|
+
function anthropicSseMessageStartRewrite(rewrite) {
|
|
11734
11765
|
const decoder = new StringDecoder("utf8");
|
|
11735
11766
|
let tail = "";
|
|
11736
11767
|
const rewriteLine = (line) => {
|
|
11737
11768
|
if (!line.startsWith("data:") || !line.includes('"message_start"')) return line;
|
|
11738
11769
|
try {
|
|
11739
11770
|
const parsed = JSON.parse(line.slice(5));
|
|
11740
|
-
if (parsed.type
|
|
11741
|
-
|
|
11742
|
-
|
|
11771
|
+
if (parsed.type !== "message_start" || !parsed.message) return line;
|
|
11772
|
+
let changed = false;
|
|
11773
|
+
if (rewrite.model !== void 0 && typeof parsed.message.model === "string") {
|
|
11774
|
+
parsed.message.model = rewrite.model;
|
|
11775
|
+
changed = true;
|
|
11776
|
+
}
|
|
11777
|
+
if (rewrite.anchorSafeId) {
|
|
11778
|
+
const id = anchorSafeMessageId(parsed.message.id);
|
|
11779
|
+
if (id !== parsed.message.id) {
|
|
11780
|
+
parsed.message.id = id;
|
|
11781
|
+
changed = true;
|
|
11782
|
+
}
|
|
11743
11783
|
}
|
|
11784
|
+
if (changed) return "data: " + JSON.stringify(parsed);
|
|
11744
11785
|
} catch {
|
|
11745
11786
|
}
|
|
11746
11787
|
return line;
|
|
@@ -11806,8 +11847,11 @@ async function relayAnthropicMessages(res, messagesUrl, body, apiKey, clientWant
|
|
|
11806
11847
|
"Connection": "keep-alive"
|
|
11807
11848
|
});
|
|
11808
11849
|
const upstream = Readable.fromWeb(upstreamRes.body).on("error", () => res.destroy());
|
|
11809
|
-
if (options.responseModelOverride) {
|
|
11810
|
-
upstream.pipe(
|
|
11850
|
+
if (options.responseModelOverride || options.anchorSafeMessageIds) {
|
|
11851
|
+
upstream.pipe(anthropicSseMessageStartRewrite({
|
|
11852
|
+
model: options.responseModelOverride,
|
|
11853
|
+
anchorSafeId: options.anchorSafeMessageIds
|
|
11854
|
+
})).on("error", () => res.destroy()).pipe(res);
|
|
11811
11855
|
} else {
|
|
11812
11856
|
upstream.pipe(res);
|
|
11813
11857
|
}
|
|
@@ -11827,9 +11871,21 @@ async function relayAnthropicMessages(res, messagesUrl, body, apiKey, clientWant
|
|
|
11827
11871
|
res.end(JSON.stringify({ type: "error", error: { type: "api_error", message: "Upstream response was not valid JSON" } }));
|
|
11828
11872
|
return;
|
|
11829
11873
|
}
|
|
11830
|
-
if (
|
|
11831
|
-
|
|
11832
|
-
|
|
11874
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed) && parsed.type === "message") {
|
|
11875
|
+
const message = parsed;
|
|
11876
|
+
let changed = false;
|
|
11877
|
+
if (options.responseModelOverride && typeof message.model === "string") {
|
|
11878
|
+
message.model = options.responseModelOverride;
|
|
11879
|
+
changed = true;
|
|
11880
|
+
}
|
|
11881
|
+
if (options.anchorSafeMessageIds) {
|
|
11882
|
+
const id = anchorSafeMessageId(message.id);
|
|
11883
|
+
if (id !== message.id) {
|
|
11884
|
+
message.id = id;
|
|
11885
|
+
changed = true;
|
|
11886
|
+
}
|
|
11887
|
+
}
|
|
11888
|
+
if (changed) text5 = JSON.stringify(message);
|
|
11833
11889
|
}
|
|
11834
11890
|
res.writeHead(200, {
|
|
11835
11891
|
"Content-Type": "application/json",
|
|
@@ -11839,10 +11895,10 @@ async function relayAnthropicMessages(res, messagesUrl, body, apiKey, clientWant
|
|
|
11839
11895
|
}
|
|
11840
11896
|
|
|
11841
11897
|
// src/proxy.ts
|
|
11842
|
-
import { randomUUID as
|
|
11898
|
+
import { randomUUID as randomUUID6 } from "crypto";
|
|
11843
11899
|
|
|
11844
11900
|
// src/sdk-adapter.ts
|
|
11845
|
-
import { createHash as createHash7 } from "crypto";
|
|
11901
|
+
import { createHash as createHash7, randomUUID as randomUUID5 } from "crypto";
|
|
11846
11902
|
import { streamText, generateText, tool, jsonSchema } from "ai";
|
|
11847
11903
|
|
|
11848
11904
|
// src/proxy-shared.ts
|
|
@@ -12690,8 +12746,11 @@ function forwardAbortSignal(source, target) {
|
|
|
12690
12746
|
source.addEventListener("abort", forward, { once: true });
|
|
12691
12747
|
return () => source.removeEventListener("abort", forward);
|
|
12692
12748
|
}
|
|
12749
|
+
function translatedMessageId() {
|
|
12750
|
+
return "clodex_" + randomUUID5().replace(/-/g, "");
|
|
12751
|
+
}
|
|
12693
12752
|
async function writeAnthropicStream(stream, modelId, write, log12, observer, tools) {
|
|
12694
|
-
const messageId =
|
|
12753
|
+
const messageId = translatedMessageId();
|
|
12695
12754
|
const requiredProps = toolRequiredProps(tools);
|
|
12696
12755
|
let blockIndex = -1;
|
|
12697
12756
|
let started = false;
|
|
@@ -13049,7 +13108,7 @@ async function generateAnthropicResponse(model, params, modelId, options) {
|
|
|
13049
13108
|
reportPromptTokens({ onPromptTokens: options?.onPromptTokens }, usage);
|
|
13050
13109
|
const requiredProps = toolRequiredProps(params.tools);
|
|
13051
13110
|
return {
|
|
13052
|
-
id:
|
|
13111
|
+
id: translatedMessageId(),
|
|
13053
13112
|
type: "message",
|
|
13054
13113
|
role: "assistant",
|
|
13055
13114
|
model: modelId,
|
|
@@ -13265,7 +13324,7 @@ function configuredAliasLookupNames(alias) {
|
|
|
13265
13324
|
}))].map(normalizeRouteLookupId);
|
|
13266
13325
|
}
|
|
13267
13326
|
async function startProxyCatalog(routes, defaultAliasId, debug = false, inferenceLogPath, debugLogPath, webSocketDiagnosticsLogPath, modelAliases) {
|
|
13268
|
-
const proxyToken =
|
|
13327
|
+
const proxyToken = randomUUID6();
|
|
13269
13328
|
silenceSdkWarnings();
|
|
13270
13329
|
if (routes.length === 0) {
|
|
13271
13330
|
throw new Error("Proxy catalog requires at least one route");
|
|
@@ -13417,6 +13476,13 @@ async function startProxyCatalog(routes, defaultAliasId, debug = false, inferenc
|
|
|
13417
13476
|
}
|
|
13418
13477
|
return;
|
|
13419
13478
|
}
|
|
13479
|
+
const holdsThreads = route.modelFormat === "anthropic" && upstreamHoldsThreads(upstreamUrl);
|
|
13480
|
+
if (!holdsThreads && isThreadContinuation(anthropicBody)) {
|
|
13481
|
+
plog(() => `thread continuation refused: route=${route.realModelId} upstream does not hold threads`);
|
|
13482
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
13483
|
+
res.end(THREAD_UNSUPPORTED_BODY);
|
|
13484
|
+
return;
|
|
13485
|
+
}
|
|
13420
13486
|
if (!apiKey && routeAuthType !== "none" && !usesSdkAdapter) {
|
|
13421
13487
|
anthropicError(res, 401, "Missing API key");
|
|
13422
13488
|
return;
|
|
@@ -13470,6 +13536,9 @@ async function startProxyCatalog(routes, defaultAliasId, debug = false, inferenc
|
|
|
13470
13536
|
// the client would believe it reached a model it never reached, and
|
|
13471
13537
|
// would key its context window off that phantom id.
|
|
13472
13538
|
responseModelOverride: resolvedRoute && typeof originalModel === "string" && originalModel !== route.realModelId ? originalModel : void 0,
|
|
13539
|
+
// An upstream that holds no threads must not hand Claude Code an id
|
|
13540
|
+
// it would anchor the next request on.
|
|
13541
|
+
anchorSafeMessageIds: !holdsThreads,
|
|
13473
13542
|
onUpstreamError: inferenceLogPath ? (statusCode, errorContent) => writeInferenceResponseErrorLog(inferenceLogPath, {
|
|
13474
13543
|
modelId: originalModel,
|
|
13475
13544
|
provider: route.providerId ?? route.aliasId.split(":")[1] ?? "unknown",
|
|
@@ -13678,7 +13747,7 @@ async function startProxyCatalog(routes, defaultAliasId, debug = false, inferenc
|
|
|
13678
13747
|
res,
|
|
13679
13748
|
upstreamStatus === 500 ? 502 : upstreamStatus,
|
|
13680
13749
|
clientMessage,
|
|
13681
|
-
contextLengthExceeded ? relayRequestId ??
|
|
13750
|
+
contextLengthExceeded ? relayRequestId ?? randomUUID6() : void 0
|
|
13682
13751
|
);
|
|
13683
13752
|
} else {
|
|
13684
13753
|
const errorType = anthropicErrorType(upstreamStatus, details?.transportCode);
|
|
@@ -13686,7 +13755,7 @@ async function startProxyCatalog(routes, defaultAliasId, debug = false, inferenc
|
|
|
13686
13755
|
data: ${JSON.stringify({
|
|
13687
13756
|
type: "error",
|
|
13688
13757
|
error: { type: errorType, message: clientMessage },
|
|
13689
|
-
...contextLengthExceeded ? { request_id: relayRequestId ??
|
|
13758
|
+
...contextLengthExceeded ? { request_id: relayRequestId ?? randomUUID6() } : {}
|
|
13690
13759
|
})}
|
|
13691
13760
|
|
|
13692
13761
|
`);
|
|
@@ -15009,7 +15078,7 @@ function localProvidersToServerModels(localProviders) {
|
|
|
15009
15078
|
}
|
|
15010
15079
|
|
|
15011
15080
|
// src/registry/credential-cleanup-journal.ts
|
|
15012
|
-
import { randomUUID as
|
|
15081
|
+
import { randomUUID as randomUUID7 } from "crypto";
|
|
15013
15082
|
import {
|
|
15014
15083
|
closeSync as closeSync9,
|
|
15015
15084
|
existsSync as existsSync5,
|
|
@@ -15149,7 +15218,7 @@ function writeJournalUnlocked(journal, path) {
|
|
|
15149
15218
|
assertRegistryWriteOwnership(path);
|
|
15150
15219
|
ensureSecureAppHome();
|
|
15151
15220
|
mkdirSync6(dirname6(path), { recursive: true, mode: DIR_MODE2 });
|
|
15152
|
-
const tmp = `${path}.${process.pid}.${
|
|
15221
|
+
const tmp = `${path}.${process.pid}.${randomUUID7()}.tmp`;
|
|
15153
15222
|
let fd;
|
|
15154
15223
|
try {
|
|
15155
15224
|
fd = openSync9(tmp, "wx", FILE_MODE4);
|
|
@@ -18042,7 +18111,7 @@ async function askSaveServerPassword() {
|
|
|
18042
18111
|
|
|
18043
18112
|
// src/server/router.ts
|
|
18044
18113
|
import { createServer as createServer2 } from "http";
|
|
18045
|
-
import { randomUUID as
|
|
18114
|
+
import { randomUUID as randomUUID8 } from "crypto";
|
|
18046
18115
|
|
|
18047
18116
|
// src/openai-adapter.ts
|
|
18048
18117
|
import { tool as tool2, jsonSchema as jsonSchema2, streamText as streamText2, generateText as generateText2 } from "ai";
|
|
@@ -18458,7 +18527,7 @@ async function handleAnthropicMessages(req, res, options, modelCache, plog) {
|
|
|
18458
18527
|
plog(`model not found: ${body.model}`);
|
|
18459
18528
|
return;
|
|
18460
18529
|
}
|
|
18461
|
-
const requestId =
|
|
18530
|
+
const requestId = randomUUID8();
|
|
18462
18531
|
const claudeSessionIdHeader = Array.isArray(req.headers["x-claude-code-session-id"]) ? req.headers["x-claude-code-session-id"][0] : req.headers["x-claude-code-session-id"];
|
|
18463
18532
|
const claudeSessionId = extractClaudeSessionId(body, claudeSessionIdHeader);
|
|
18464
18533
|
const claudeAgentIds = extractClaudeAgentIds(req.headers);
|
|
@@ -19132,7 +19201,7 @@ import * as http2 from "http";
|
|
|
19132
19201
|
import * as https from "https";
|
|
19133
19202
|
import * as net from "net";
|
|
19134
19203
|
import { PassThrough } from "stream";
|
|
19135
|
-
import { randomUUID as
|
|
19204
|
+
import { randomUUID as randomUUID9 } from "crypto";
|
|
19136
19205
|
import { URL as URL2 } from "url";
|
|
19137
19206
|
import { createBrotliDecompress, createGunzip, createInflate } from "zlib";
|
|
19138
19207
|
|
|
@@ -19990,7 +20059,7 @@ async function startHttpProxy(options) {
|
|
|
19990
20059
|
}
|
|
19991
20060
|
const messagesEndpoint = anthropicMessagesEndpoint(req.url);
|
|
19992
20061
|
if (req.method === "POST" && messagesEndpoint) {
|
|
19993
|
-
const requestId =
|
|
20062
|
+
const requestId = randomUUID9();
|
|
19994
20063
|
let parsed = null;
|
|
19995
20064
|
let route;
|
|
19996
20065
|
let adapterBody = rawBody;
|