@bman654/clodex 2.0.0 → 2.1.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 +1 -1
- package/dist/{chunk-ZYGZ5GDI.js → chunk-QLGIKUKC.js} +47 -1
- package/dist/chunk-QLGIKUKC.js.map +1 -0
- package/dist/claude-wrapper.js +4 -27
- package/dist/claude-wrapper.js.map +1 -1
- package/dist/cli.js +207 -109
- package/dist/cli.js.map +1 -1
- package/docs/background-agents.md +3 -0
- package/package.json +1 -1
- package/dist/chunk-ZYGZ5GDI.js.map +0 -1
package/dist/cli.js
CHANGED
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
loadRegistryStrict,
|
|
21
21
|
recordLaunchSelection,
|
|
22
22
|
registerServerRuntimeState,
|
|
23
|
+
removeAnthropicProxyBypass,
|
|
23
24
|
resolveBridgeMode,
|
|
24
25
|
savePreferences,
|
|
25
26
|
saveRegistry,
|
|
@@ -32,7 +33,7 @@ import {
|
|
|
32
33
|
withCredentialMutationLock,
|
|
33
34
|
withRegistryWriteLock,
|
|
34
35
|
withRegistryWriteLockSync
|
|
35
|
-
} from "./chunk-
|
|
36
|
+
} from "./chunk-QLGIKUKC.js";
|
|
36
37
|
|
|
37
38
|
// src/cli.ts
|
|
38
39
|
import pc13 from "picocolors";
|
|
@@ -210,7 +211,7 @@ import { join } from "path";
|
|
|
210
211
|
// package.json
|
|
211
212
|
var package_default = {
|
|
212
213
|
name: "@bman654/clodex",
|
|
213
|
-
version: "2.
|
|
214
|
+
version: "2.1.0",
|
|
214
215
|
publishConfig: {
|
|
215
216
|
access: "public"
|
|
216
217
|
},
|
|
@@ -544,6 +545,10 @@ var ONE_M_CONTEXT_WINDOW = 1e6;
|
|
|
544
545
|
function stripOneMContextSuffix(modelId) {
|
|
545
546
|
return modelId.replace(/\[1m\]$/i, "");
|
|
546
547
|
}
|
|
548
|
+
function normalizeRouteLookupId(id) {
|
|
549
|
+
const bare = stripOneMContextSuffix(id);
|
|
550
|
+
return bare.startsWith("models/") ? bare.slice("models/".length) : bare;
|
|
551
|
+
}
|
|
547
552
|
function claudeCodeClientModelId(modelId, contextWindow) {
|
|
548
553
|
const bare = stripOneMContextSuffix(modelId);
|
|
549
554
|
const window = resolveContextWindow(bare, contextWindow);
|
|
@@ -552,19 +557,6 @@ function claudeCodeClientModelId(modelId, contextWindow) {
|
|
|
552
557
|
}
|
|
553
558
|
return bare;
|
|
554
559
|
}
|
|
555
|
-
function routeLookupIds(id) {
|
|
556
|
-
const bare = stripOneMContextSuffix(id);
|
|
557
|
-
const googleBare = bare.startsWith("models/") ? bare.slice("models/".length) : bare;
|
|
558
|
-
return [.../* @__PURE__ */ new Set([
|
|
559
|
-
id,
|
|
560
|
-
bare,
|
|
561
|
-
`${bare}${ONE_M_CONTEXT_SUFFIX}`,
|
|
562
|
-
googleBare,
|
|
563
|
-
`${googleBare}${ONE_M_CONTEXT_SUFFIX}`,
|
|
564
|
-
`models/${googleBare}`,
|
|
565
|
-
`models/${bare}`
|
|
566
|
-
])];
|
|
567
|
-
}
|
|
568
560
|
|
|
569
561
|
// src/oauth/types.ts
|
|
570
562
|
function oauthCredentialToKeychainJson(cred) {
|
|
@@ -830,24 +822,7 @@ function buildHttpProxyChildEnv(proxyPort, caCertPath) {
|
|
|
830
822
|
env["https_proxy"] = proxyUrl;
|
|
831
823
|
env["http_proxy"] = proxyUrl;
|
|
832
824
|
env["NODE_EXTRA_CA_CERTS"] = caCertPath;
|
|
833
|
-
|
|
834
|
-
if (noProxy !== void 0) {
|
|
835
|
-
const filtered = noProxy.split(",").map((value) => value.trim()).filter(Boolean).filter((value) => {
|
|
836
|
-
const entry = value.toLowerCase().replace(/^https?:\/\//, "");
|
|
837
|
-
const host = entry.replace(/:\d+$/, "");
|
|
838
|
-
if (host === "*") return false;
|
|
839
|
-
const suffix = host.startsWith("*.") ? host.slice(1) : host;
|
|
840
|
-
const bypassesAnthropic = suffix.startsWith(".") ? "api.anthropic.com".endsWith(suffix) : "api.anthropic.com" === suffix || "api.anthropic.com".endsWith(`.${suffix}`);
|
|
841
|
-
return !bypassesAnthropic;
|
|
842
|
-
}).join(",");
|
|
843
|
-
if (filtered) {
|
|
844
|
-
env["NO_PROXY"] = filtered;
|
|
845
|
-
env["no_proxy"] = filtered;
|
|
846
|
-
} else {
|
|
847
|
-
delete env["NO_PROXY"];
|
|
848
|
-
delete env["no_proxy"];
|
|
849
|
-
}
|
|
850
|
-
}
|
|
825
|
+
removeAnthropicProxyBypass(env);
|
|
851
826
|
return env;
|
|
852
827
|
}
|
|
853
828
|
function classifyKeyringError(err) {
|
|
@@ -5062,6 +5037,7 @@ function nonNegativeInteger(value) {
|
|
|
5062
5037
|
return value !== void 0 && Number.isFinite(value) ? Math.max(0, Math.round(value)) : void 0;
|
|
5063
5038
|
}
|
|
5064
5039
|
function writeInferenceResponseLifecycleLog(path, entry) {
|
|
5040
|
+
const claudeSessionId = safeClaudeSessionId(entry.claudeSessionId);
|
|
5065
5041
|
const statusCode = nonNegativeInteger(entry.statusCode);
|
|
5066
5042
|
const durationMs = nonNegativeInteger(entry.durationMs);
|
|
5067
5043
|
const timeToFirstByteMs = nonNegativeInteger(entry.timeToFirstByteMs);
|
|
@@ -5081,6 +5057,7 @@ function writeInferenceResponseLifecycleLog(path, entry) {
|
|
|
5081
5057
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
5082
5058
|
event: entry.event,
|
|
5083
5059
|
requestId: compactLogValue(entry.requestId, 100),
|
|
5060
|
+
...claudeSessionId ? { claudeSessionId } : {},
|
|
5084
5061
|
modelId: compactLogValue(entry.modelId),
|
|
5085
5062
|
provider: compactLogValue(entry.provider, 200),
|
|
5086
5063
|
route: entry.route,
|
|
@@ -5103,7 +5080,8 @@ function writeInferenceResponseLifecycleLog(path, entry) {
|
|
|
5103
5080
|
...cacheReadInputTokens !== void 0 ? { cacheReadInputTokens } : {},
|
|
5104
5081
|
...entry.lastPartType ? { lastPartType: compactLogValue(entry.lastPartType, 100) } : {},
|
|
5105
5082
|
...entry.errorType ? { errorType: compactLogValue(entry.errorType, 200) } : {},
|
|
5106
|
-
...entry.errorSignature ? { errorSignature: compactLogValue(entry.errorSignature, 100) } : {}
|
|
5083
|
+
...entry.errorSignature ? { errorSignature: compactLogValue(entry.errorSignature, 100) } : {},
|
|
5084
|
+
...entry.terminationSource ? { terminationSource: entry.terminationSource } : {}
|
|
5107
5085
|
}));
|
|
5108
5086
|
}
|
|
5109
5087
|
function writeProxyLifecycleLog(path, entry) {
|
|
@@ -5154,6 +5132,15 @@ function writeInferenceResponseErrorLog(path, entry) {
|
|
|
5154
5132
|
...includeContent ? { errorContent: compactLogValueWithMarker(entry.errorContent, RESPONSE_ERROR_MAX) } : {}
|
|
5155
5133
|
}));
|
|
5156
5134
|
}
|
|
5135
|
+
function writeInferenceRouteUnavailableLog(path, entry) {
|
|
5136
|
+
writeSecureLogLine(path, JSON.stringify({
|
|
5137
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
5138
|
+
event: "route_unavailable",
|
|
5139
|
+
requestId: compactLogValue(entry.requestId, 100),
|
|
5140
|
+
modelId: compactLogValue(entry.modelId),
|
|
5141
|
+
statusCode: entry.statusCode
|
|
5142
|
+
}));
|
|
5143
|
+
}
|
|
5157
5144
|
function makeTraceLogger(logPath) {
|
|
5158
5145
|
resetTraceLog(logPath);
|
|
5159
5146
|
return (message) => writeSecureLogLine(logPath, `${(/* @__PURE__ */ new Date()).toISOString()} ${message}`);
|
|
@@ -7309,6 +7296,38 @@ function sendJson(res, status, body) {
|
|
|
7309
7296
|
res.end(json);
|
|
7310
7297
|
}
|
|
7311
7298
|
|
|
7299
|
+
// src/model-aliases.ts
|
|
7300
|
+
var MODEL_ALIAS_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/;
|
|
7301
|
+
function isValidModelAlias(name) {
|
|
7302
|
+
return MODEL_ALIAS_PATTERN.test(name);
|
|
7303
|
+
}
|
|
7304
|
+
function parseModelAliasAssignment(value) {
|
|
7305
|
+
const separator = value.indexOf("=");
|
|
7306
|
+
if (separator < 1 || separator === value.length - 1) {
|
|
7307
|
+
return { error: "Alias must use name=clodex:<provider-id>:<model-id>." };
|
|
7308
|
+
}
|
|
7309
|
+
const name = value.slice(0, separator).trim();
|
|
7310
|
+
if (!isValidModelAlias(name)) {
|
|
7311
|
+
return { error: "Alias names must be 1-64 letters, numbers, dots, underscores, or hyphens." };
|
|
7312
|
+
}
|
|
7313
|
+
const rawTarget = value.slice(separator + 1).trim();
|
|
7314
|
+
const target = rawTarget.startsWith("clodex:") ? rawTarget.slice("clodex:".length) : rawTarget;
|
|
7315
|
+
const targetSeparator = target.indexOf(":");
|
|
7316
|
+
if (targetSeparator < 1 || targetSeparator === target.length - 1) {
|
|
7317
|
+
return { error: "Alias target must use clodex:<provider-id>:<model-id>." };
|
|
7318
|
+
}
|
|
7319
|
+
return {
|
|
7320
|
+
name,
|
|
7321
|
+
providerId: target.slice(0, targetSeparator),
|
|
7322
|
+
// `models --list` prints Claude's synthetic context suffix. It is a client
|
|
7323
|
+
// routing hint, not part of the provider catalog id stored in favorites.
|
|
7324
|
+
modelId: stripOneMContextSuffix(target.slice(targetSeparator + 1))
|
|
7325
|
+
};
|
|
7326
|
+
}
|
|
7327
|
+
function modelAliasTarget(alias) {
|
|
7328
|
+
return `clodex:${alias.providerId}:${alias.modelId}`;
|
|
7329
|
+
}
|
|
7330
|
+
|
|
7312
7331
|
// src/catalog.ts
|
|
7313
7332
|
function localModelToRoute(lp, model) {
|
|
7314
7333
|
if (model.modelFormat === "anthropic" && !model.baseUrl) return null;
|
|
@@ -7349,6 +7368,12 @@ function makeRouteResolver(localProviders) {
|
|
|
7349
7368
|
return provider && model ? localModelToRoute(provider, model) ?? void 0 : void 0;
|
|
7350
7369
|
};
|
|
7351
7370
|
}
|
|
7371
|
+
function resolveCatalogModelAliases(modelAliases, resolveRoute) {
|
|
7372
|
+
return modelAliases.map((alias) => ({
|
|
7373
|
+
name: alias.name,
|
|
7374
|
+
routeId: resolveRoute(alias.providerId, alias.modelId)?.aliasId ?? modelAliasTarget(alias)
|
|
7375
|
+
}));
|
|
7376
|
+
}
|
|
7352
7377
|
function buildCatalogRoutes(startingRoute, favorites, resolveRoute, max = MAX_MODEL_CATALOG) {
|
|
7353
7378
|
const droppedFavorites = [];
|
|
7354
7379
|
const tail = favorites.map((fav) => {
|
|
@@ -7363,38 +7388,6 @@ function buildCatalogRoutes(startingRoute, favorites, resolveRoute, max = MAX_MO
|
|
|
7363
7388
|
return { routes, droppedFavorites };
|
|
7364
7389
|
}
|
|
7365
7390
|
|
|
7366
|
-
// src/model-aliases.ts
|
|
7367
|
-
var MODEL_ALIAS_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/;
|
|
7368
|
-
function isValidModelAlias(name) {
|
|
7369
|
-
return MODEL_ALIAS_PATTERN.test(name);
|
|
7370
|
-
}
|
|
7371
|
-
function parseModelAliasAssignment(value) {
|
|
7372
|
-
const separator = value.indexOf("=");
|
|
7373
|
-
if (separator < 1 || separator === value.length - 1) {
|
|
7374
|
-
return { error: "Alias must use name=clodex:<provider-id>:<model-id>." };
|
|
7375
|
-
}
|
|
7376
|
-
const name = value.slice(0, separator).trim();
|
|
7377
|
-
if (!isValidModelAlias(name)) {
|
|
7378
|
-
return { error: "Alias names must be 1-64 letters, numbers, dots, underscores, or hyphens." };
|
|
7379
|
-
}
|
|
7380
|
-
const rawTarget = value.slice(separator + 1).trim();
|
|
7381
|
-
const target = rawTarget.startsWith("clodex:") ? rawTarget.slice("clodex:".length) : rawTarget;
|
|
7382
|
-
const targetSeparator = target.indexOf(":");
|
|
7383
|
-
if (targetSeparator < 1 || targetSeparator === target.length - 1) {
|
|
7384
|
-
return { error: "Alias target must use clodex:<provider-id>:<model-id>." };
|
|
7385
|
-
}
|
|
7386
|
-
return {
|
|
7387
|
-
name,
|
|
7388
|
-
providerId: target.slice(0, targetSeparator),
|
|
7389
|
-
// `models --list` prints Claude's synthetic context suffix. It is a client
|
|
7390
|
-
// routing hint, not part of the provider catalog id stored in favorites.
|
|
7391
|
-
modelId: stripOneMContextSuffix(target.slice(targetSeparator + 1))
|
|
7392
|
-
};
|
|
7393
|
-
}
|
|
7394
|
-
function modelAliasTarget(alias) {
|
|
7395
|
-
return `clodex:${alias.providerId}:${alias.modelId}`;
|
|
7396
|
-
}
|
|
7397
|
-
|
|
7398
7391
|
// src/http-proxy/routes.ts
|
|
7399
7392
|
var HTTP_PROXY_MODEL_PREFIX = "clodex:";
|
|
7400
7393
|
function httpProxyModelId(providerId, modelId) {
|
|
@@ -7578,6 +7571,11 @@ function formatOpenAIModels(models) {
|
|
|
7578
7571
|
};
|
|
7579
7572
|
}
|
|
7580
7573
|
|
|
7574
|
+
// src/route-unavailable.ts
|
|
7575
|
+
function routeUnavailableMessage(modelId) {
|
|
7576
|
+
return `Clodex model route '${modelId}' is unavailable. Run \`clodex models --list\` to see available routes, or \`clodex patch\` to refresh saved aliases.`;
|
|
7577
|
+
}
|
|
7578
|
+
|
|
7581
7579
|
// src/upstream-forward.ts
|
|
7582
7580
|
import { Readable } from "stream";
|
|
7583
7581
|
|
|
@@ -8678,7 +8676,7 @@ async function listenTcpServer(server, port, host) {
|
|
|
8678
8676
|
// src/proxy.ts
|
|
8679
8677
|
var STREAM_KEEPALIVE_INTERVAL_MS = 2e4;
|
|
8680
8678
|
var STREAM_KEEPALIVE_PING = 'event: ping\ndata: {"type":"ping"}\n\n';
|
|
8681
|
-
function createTranslationLifecycle(logPath, requestId, modelId, provider) {
|
|
8679
|
+
function createTranslationLifecycle(logPath, requestId, claudeSessionId, modelId, provider) {
|
|
8682
8680
|
if (!logPath || !requestId) return void 0;
|
|
8683
8681
|
const startedAt = Date.now();
|
|
8684
8682
|
let firstPartAt;
|
|
@@ -8693,6 +8691,7 @@ function createTranslationLifecycle(logPath, requestId, modelId, provider) {
|
|
|
8693
8691
|
const write = (event, extra = {}) => writeInferenceResponseLifecycleLog(logPath, {
|
|
8694
8692
|
event,
|
|
8695
8693
|
requestId,
|
|
8694
|
+
claudeSessionId,
|
|
8696
8695
|
modelId,
|
|
8697
8696
|
provider,
|
|
8698
8697
|
route: "translated",
|
|
@@ -8798,11 +8797,7 @@ function aliasModelId(realId, providerId) {
|
|
|
8798
8797
|
return `anthropic-${sanitized}__${realId}`;
|
|
8799
8798
|
}
|
|
8800
8799
|
function lookupRoute(byAlias, id) {
|
|
8801
|
-
|
|
8802
|
-
const route = byAlias.get(key);
|
|
8803
|
-
if (route) return route;
|
|
8804
|
-
}
|
|
8805
|
-
return void 0;
|
|
8800
|
+
return byAlias.get(normalizeRouteLookupId(id));
|
|
8806
8801
|
}
|
|
8807
8802
|
async function startProxyCatalog(routes, defaultAliasId, debug = false, inferenceLogPath, debugLogPath, webSocketDiagnosticsLogPath, modelAliases) {
|
|
8808
8803
|
const proxyToken = randomUUID6();
|
|
@@ -8810,12 +8805,16 @@ async function startProxyCatalog(routes, defaultAliasId, debug = false, inferenc
|
|
|
8810
8805
|
if (routes.length === 0) {
|
|
8811
8806
|
throw new Error("Proxy catalog requires at least one route");
|
|
8812
8807
|
}
|
|
8813
|
-
const byAlias = new Map(routes.map((r) => [r.aliasId, r]));
|
|
8808
|
+
const byAlias = new Map(routes.map((r) => [normalizeRouteLookupId(r.aliasId), r]));
|
|
8809
|
+
const configuredAliasNames = new Set(
|
|
8810
|
+
(modelAliases ?? []).map((alias) => normalizeRouteLookupId(alias.name))
|
|
8811
|
+
);
|
|
8814
8812
|
for (const alias of modelAliases ?? []) {
|
|
8815
|
-
const route = byAlias
|
|
8816
|
-
|
|
8813
|
+
const route = lookupRoute(byAlias, alias.routeId);
|
|
8814
|
+
const aliasId = normalizeRouteLookupId(alias.name);
|
|
8815
|
+
if (route && !byAlias.has(aliasId)) byAlias.set(aliasId, route);
|
|
8817
8816
|
}
|
|
8818
|
-
const defaultRoute = byAlias
|
|
8817
|
+
const defaultRoute = lookupRoute(byAlias, defaultAliasId) ?? routes[0];
|
|
8819
8818
|
const plog = makeProxyLog(debug, debugLogPath);
|
|
8820
8819
|
const onRejection = (reason) => {
|
|
8821
8820
|
plog(() => `Unhandled Rejection: ${reason instanceof Error ? reason.stack || reason.message : String(reason)}`);
|
|
@@ -8882,7 +8881,13 @@ async function startProxyCatalog(routes, defaultAliasId, debug = false, inferenc
|
|
|
8882
8881
|
const clientWantsStream = Boolean(anthropicBody.stream);
|
|
8883
8882
|
const relayRequestIdRaw = req.headers["x-relay-request-id"];
|
|
8884
8883
|
const relayRequestId = Array.isArray(relayRequestIdRaw) ? relayRequestIdRaw[0] : relayRequestIdRaw;
|
|
8885
|
-
const
|
|
8884
|
+
const resolvedRoute = typeof originalModel === "string" ? lookupRoute(byAlias, originalModel) : void 0;
|
|
8885
|
+
const configuredModelUnavailable = typeof originalModel === "string" && (normalizeRouteLookupId(originalModel).startsWith("clodex:") || configuredAliasNames.has(normalizeRouteLookupId(originalModel)));
|
|
8886
|
+
if (!resolvedRoute && configuredModelUnavailable) {
|
|
8887
|
+
anthropicError(res, 400, routeUnavailableMessage(originalModel));
|
|
8888
|
+
return;
|
|
8889
|
+
}
|
|
8890
|
+
const route = resolvedRoute ?? defaultRoute;
|
|
8886
8891
|
if (messagesEndpoint === "count_tokens" && route.modelFormat !== "anthropic") {
|
|
8887
8892
|
const inputTokens = estimateAnthropicInputTokens(anthropicBody);
|
|
8888
8893
|
plog(() => `token-count: local estimate model=${originalModel} input_tokens=${inputTokens}`);
|
|
@@ -8994,15 +8999,16 @@ async function startProxyCatalog(routes, defaultAliasId, debug = false, inferenc
|
|
|
8994
8999
|
}
|
|
8995
9000
|
if (usesSdkAdapter) {
|
|
8996
9001
|
const openAiOAuth = route.npm === "@ai-sdk/openai" && route.authType === "oauth";
|
|
9002
|
+
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"];
|
|
9003
|
+
const claudeSessionId = extractClaudeSessionId(anthropicBody, claudeSessionIdHeader);
|
|
8997
9004
|
const translationLifecycle = createTranslationLifecycle(
|
|
8998
9005
|
inferenceLogPath,
|
|
8999
9006
|
relayRequestId,
|
|
9007
|
+
claudeSessionId,
|
|
9000
9008
|
originalModel,
|
|
9001
9009
|
route.providerId ?? route.aliasId.split(":")[1] ?? "unknown"
|
|
9002
9010
|
);
|
|
9003
9011
|
const runSdkRequest = async () => {
|
|
9004
|
-
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"];
|
|
9005
|
-
const claudeSessionId = extractClaudeSessionId(anthropicBody, claudeSessionIdHeader);
|
|
9006
9012
|
const params = translateRequest(anthropicBody, route.npm, {
|
|
9007
9013
|
openAiOAuth,
|
|
9008
9014
|
claudeSessionId,
|
|
@@ -10528,7 +10534,7 @@ function requestHeadersWithoutProxyHeaders(req) {
|
|
|
10528
10534
|
}
|
|
10529
10535
|
return headers;
|
|
10530
10536
|
}
|
|
10531
|
-
function forwardRawAnthropicRequest(req, res, rawBody, origin, rejectUnauthorized, onErrorResponse, onResponseUsage, lifecycle) {
|
|
10537
|
+
function forwardRawAnthropicRequest(req, res, rawBody, origin, rejectUnauthorized, onErrorResponse, onResponseUsage, lifecycle, isLocalShutdown = () => false) {
|
|
10532
10538
|
return new Promise((resolve2) => {
|
|
10533
10539
|
const startedAt = Date.now();
|
|
10534
10540
|
let lastActivityAt = startedAt;
|
|
@@ -10546,6 +10552,7 @@ function forwardRawAnthropicRequest(req, res, rawBody, origin, rejectUnauthorize
|
|
|
10546
10552
|
writeInferenceResponseLifecycleLog(lifecycle.logPath, {
|
|
10547
10553
|
event,
|
|
10548
10554
|
requestId: lifecycle.requestId,
|
|
10555
|
+
claudeSessionId: lifecycle.claudeSessionId,
|
|
10549
10556
|
modelId: lifecycle.modelId,
|
|
10550
10557
|
provider: lifecycle.provider,
|
|
10551
10558
|
route: "passthrough",
|
|
@@ -10628,7 +10635,8 @@ function forwardRawAnthropicRequest(req, res, rawBody, origin, rejectUnauthorize
|
|
|
10628
10635
|
idleMs: now - lastActivityAt,
|
|
10629
10636
|
bytes,
|
|
10630
10637
|
chunks,
|
|
10631
|
-
errorType: errorType(err)
|
|
10638
|
+
errorType: errorType(err),
|
|
10639
|
+
terminationSource: "upstream_failure"
|
|
10632
10640
|
});
|
|
10633
10641
|
done();
|
|
10634
10642
|
});
|
|
@@ -10657,7 +10665,8 @@ function forwardRawAnthropicRequest(req, res, rawBody, origin, rejectUnauthorize
|
|
|
10657
10665
|
...firstByteAt !== void 0 ? { timeToFirstByteMs: firstByteAt - startedAt } : {},
|
|
10658
10666
|
idleMs: now - lastActivityAt,
|
|
10659
10667
|
bytes,
|
|
10660
|
-
chunks
|
|
10668
|
+
chunks,
|
|
10669
|
+
terminationSource: isLocalShutdown() ? "local_shutdown" : "downstream_client"
|
|
10661
10670
|
});
|
|
10662
10671
|
upstream.destroy(new Error("Client disconnected"));
|
|
10663
10672
|
done();
|
|
@@ -10677,7 +10686,8 @@ function forwardRawAnthropicRequest(req, res, rawBody, origin, rejectUnauthorize
|
|
|
10677
10686
|
idleMs: now - lastActivityAt,
|
|
10678
10687
|
bytes,
|
|
10679
10688
|
chunks,
|
|
10680
|
-
errorType: errorType(err)
|
|
10689
|
+
errorType: errorType(err),
|
|
10690
|
+
terminationSource: "upstream_failure"
|
|
10681
10691
|
});
|
|
10682
10692
|
onErrorResponse?.(502, `Anthropic upstream unreachable: ${err.message}`);
|
|
10683
10693
|
if (!res.headersSent) res.writeHead(502, { "Content-Type": "text/plain" });
|
|
@@ -10687,7 +10697,7 @@ function forwardRawAnthropicRequest(req, res, rawBody, origin, rejectUnauthorize
|
|
|
10687
10697
|
upstream.end(rawBody);
|
|
10688
10698
|
});
|
|
10689
10699
|
}
|
|
10690
|
-
function forwardToAdapter(req, res, rawBody, adapter, lifecycle) {
|
|
10700
|
+
function forwardToAdapter(req, res, rawBody, adapter, lifecycle, isLocalShutdown = () => false) {
|
|
10691
10701
|
return new Promise((resolve2) => {
|
|
10692
10702
|
const startedAt = Date.now();
|
|
10693
10703
|
let lastActivityAt = startedAt;
|
|
@@ -10706,6 +10716,7 @@ function forwardToAdapter(req, res, rawBody, adapter, lifecycle) {
|
|
|
10706
10716
|
writeInferenceResponseLifecycleLog(lifecycle.logPath, {
|
|
10707
10717
|
event,
|
|
10708
10718
|
requestId: lifecycle.requestId,
|
|
10719
|
+
claudeSessionId: lifecycle.claudeSessionId,
|
|
10709
10720
|
modelId: lifecycle.modelId,
|
|
10710
10721
|
provider: lifecycle.provider,
|
|
10711
10722
|
route: "translated",
|
|
@@ -10757,7 +10768,8 @@ function forwardToAdapter(req, res, rawBody, adapter, lifecycle) {
|
|
|
10757
10768
|
...firstByteAt !== void 0 ? { timeToFirstByteMs: firstByteAt - startedAt } : {},
|
|
10758
10769
|
idleMs: now - lastActivityAt,
|
|
10759
10770
|
bytes,
|
|
10760
|
-
chunks
|
|
10771
|
+
chunks,
|
|
10772
|
+
terminationSource: isLocalShutdown() ? "local_shutdown" : "downstream_client"
|
|
10761
10773
|
});
|
|
10762
10774
|
adapterResponse?.destroy(new Error("Client disconnected"));
|
|
10763
10775
|
upstream?.destroy(new Error("Client disconnected"));
|
|
@@ -10779,7 +10791,8 @@ function forwardToAdapter(req, res, rawBody, adapter, lifecycle) {
|
|
|
10779
10791
|
idleMs: now - lastActivityAt,
|
|
10780
10792
|
bytes,
|
|
10781
10793
|
chunks,
|
|
10782
|
-
errorType: err.name
|
|
10794
|
+
errorType: err.name,
|
|
10795
|
+
terminationSource: "upstream_failure"
|
|
10783
10796
|
});
|
|
10784
10797
|
if (!res.headersSent) res.writeHead(502, { "Content-Type": "text/plain" });
|
|
10785
10798
|
res.end(`Relay adapter unreachable: ${err.message}`);
|
|
@@ -10834,7 +10847,8 @@ function forwardToAdapter(req, res, rawBody, adapter, lifecycle) {
|
|
|
10834
10847
|
idleMs: now - lastActivityAt,
|
|
10835
10848
|
bytes,
|
|
10836
10849
|
chunks,
|
|
10837
|
-
errorType: err.name
|
|
10850
|
+
errorType: err.name,
|
|
10851
|
+
terminationSource: "upstream_failure"
|
|
10838
10852
|
});
|
|
10839
10853
|
if (!res.writableEnded) res.destroy(err);
|
|
10840
10854
|
resolve2();
|
|
@@ -10886,13 +10900,19 @@ function forwardPlainHttp(req, res) {
|
|
|
10886
10900
|
async function startHttpProxy(options) {
|
|
10887
10901
|
const certificates = ensureHttpProxyCertificates();
|
|
10888
10902
|
const routesById = /* @__PURE__ */ new Map();
|
|
10903
|
+
const reservedModelIds = /* @__PURE__ */ new Set();
|
|
10889
10904
|
for (const route of options.routes) {
|
|
10890
|
-
|
|
10905
|
+
routesById.set(normalizeRouteLookupId(route.aliasId), route);
|
|
10891
10906
|
}
|
|
10892
10907
|
for (const alias of options.modelAliases ?? []) {
|
|
10893
|
-
const
|
|
10908
|
+
const aliasId = normalizeRouteLookupId(alias.name);
|
|
10909
|
+
reservedModelIds.add(aliasId);
|
|
10910
|
+
const route = routesById.get(normalizeRouteLookupId(alias.routeId));
|
|
10894
10911
|
if (!route) continue;
|
|
10895
|
-
|
|
10912
|
+
routesById.set(aliasId, route);
|
|
10913
|
+
}
|
|
10914
|
+
for (const modelId of options.reservedModelIds ?? []) {
|
|
10915
|
+
reservedModelIds.add(normalizeRouteLookupId(modelId));
|
|
10896
10916
|
}
|
|
10897
10917
|
const anthropicOrigin = new URL2(options.anthropicOrigin ?? "https://api.anthropic.com");
|
|
10898
10918
|
let adapter = options.adapterHandle ?? null;
|
|
@@ -10907,6 +10927,7 @@ async function startHttpProxy(options) {
|
|
|
10907
10927
|
options.modelAliases
|
|
10908
10928
|
);
|
|
10909
10929
|
}
|
|
10930
|
+
let shuttingDown = false;
|
|
10910
10931
|
const mitmServer = https.createServer({
|
|
10911
10932
|
key: certificates.serverKey,
|
|
10912
10933
|
cert: certificates.serverCert,
|
|
@@ -10925,13 +10946,49 @@ async function startHttpProxy(options) {
|
|
|
10925
10946
|
const requestId = randomUUID8();
|
|
10926
10947
|
let parsed = null;
|
|
10927
10948
|
let route;
|
|
10949
|
+
let adapterBody = rawBody;
|
|
10950
|
+
const contentEncoding = (Array.isArray(req.headers["content-encoding"]) ? req.headers["content-encoding"].join(",") : req.headers["content-encoding"] ?? "").trim().toLowerCase();
|
|
10951
|
+
const encodedRequestBody = contentEncoding !== "" && contentEncoding !== "identity";
|
|
10928
10952
|
try {
|
|
10929
|
-
|
|
10930
|
-
|
|
10953
|
+
const decodedBody = decodeRequestBody(rawBody, req.headers["content-encoding"]);
|
|
10954
|
+
parsed = JSON.parse(decodedBody);
|
|
10955
|
+
if (encodedRequestBody) adapterBody = Buffer.from(decodedBody);
|
|
10956
|
+
if (typeof parsed.model === "string") {
|
|
10957
|
+
route = routesById.get(normalizeRouteLookupId(parsed.model));
|
|
10958
|
+
}
|
|
10931
10959
|
} catch {
|
|
10960
|
+
if (encodedRequestBody) {
|
|
10961
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
10962
|
+
res.end(JSON.stringify({
|
|
10963
|
+
type: "error",
|
|
10964
|
+
error: {
|
|
10965
|
+
type: "invalid_request_error",
|
|
10966
|
+
message: "Unable to inspect compressed request body"
|
|
10967
|
+
}
|
|
10968
|
+
}));
|
|
10969
|
+
return;
|
|
10970
|
+
}
|
|
10932
10971
|
}
|
|
10933
10972
|
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"];
|
|
10934
10973
|
const claudeSessionId = parsed ? extractClaudeSessionId(parsed, claudeSessionIdHeader) : void 0;
|
|
10974
|
+
const requestedModel = typeof parsed?.model === "string" ? parsed.model : void 0;
|
|
10975
|
+
const unresolvedRoutedModel = !route && requestedModel !== void 0 && (normalizeRouteLookupId(requestedModel).startsWith(HTTP_PROXY_MODEL_PREFIX) || reservedModelIds.has(normalizeRouteLookupId(requestedModel)));
|
|
10976
|
+
if (unresolvedRoutedModel) {
|
|
10977
|
+
const message = routeUnavailableMessage(requestedModel);
|
|
10978
|
+
if (messagesEndpoint === "messages" && options.inferenceLogPath) {
|
|
10979
|
+
writeInferenceRouteUnavailableLog(options.inferenceLogPath, {
|
|
10980
|
+
requestId,
|
|
10981
|
+
modelId: requestedModel,
|
|
10982
|
+
statusCode: 400
|
|
10983
|
+
});
|
|
10984
|
+
}
|
|
10985
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
10986
|
+
res.end(JSON.stringify({
|
|
10987
|
+
type: "error",
|
|
10988
|
+
error: { type: "invalid_request_error", message }
|
|
10989
|
+
}));
|
|
10990
|
+
return;
|
|
10991
|
+
}
|
|
10935
10992
|
if (messagesEndpoint === "messages" && options.inferenceLogPath) {
|
|
10936
10993
|
const provider = route ? route.providerId ?? route.aliasId.split(":")[1] ?? "unknown" : "anthropic";
|
|
10937
10994
|
writeInferenceRequestLog(options.inferenceLogPath, {
|
|
@@ -10957,13 +11014,21 @@ async function startHttpProxy(options) {
|
|
|
10957
11014
|
});
|
|
10958
11015
|
}
|
|
10959
11016
|
if (route && adapter) {
|
|
10960
|
-
await forwardToAdapter(
|
|
10961
|
-
|
|
10962
|
-
|
|
10963
|
-
|
|
10964
|
-
|
|
10965
|
-
|
|
10966
|
-
|
|
11017
|
+
await forwardToAdapter(
|
|
11018
|
+
req,
|
|
11019
|
+
res,
|
|
11020
|
+
adapterBody,
|
|
11021
|
+
adapter,
|
|
11022
|
+
messagesEndpoint === "messages" && options.inferenceLogPath ? {
|
|
11023
|
+
logPath: options.inferenceLogPath,
|
|
11024
|
+
requestId,
|
|
11025
|
+
claudeSessionId,
|
|
11026
|
+
modelId: typeof parsed?.model === "string" ? parsed.model : "unknown",
|
|
11027
|
+
provider: route.providerId ?? route.aliasId.split(":")[1] ?? "unknown",
|
|
11028
|
+
progressIntervalMs: options.responseProgressIntervalMs ?? INFERENCE_PROGRESS_INTERVAL_MS
|
|
11029
|
+
} : void 0,
|
|
11030
|
+
() => shuttingDown
|
|
11031
|
+
);
|
|
10967
11032
|
return;
|
|
10968
11033
|
}
|
|
10969
11034
|
await forwardRawAnthropicRequest(
|
|
@@ -10983,6 +11048,7 @@ async function startHttpProxy(options) {
|
|
|
10983
11048
|
messagesEndpoint === "messages" && options.inferenceLogPath ? (usage) => writeInferenceResponseLifecycleLog(options.inferenceLogPath, {
|
|
10984
11049
|
event: "response_usage",
|
|
10985
11050
|
requestId,
|
|
11051
|
+
claudeSessionId,
|
|
10986
11052
|
modelId: typeof parsed?.model === "string" ? parsed.model : "unknown",
|
|
10987
11053
|
provider: "anthropic",
|
|
10988
11054
|
route: "passthrough",
|
|
@@ -10991,10 +11057,12 @@ async function startHttpProxy(options) {
|
|
|
10991
11057
|
messagesEndpoint === "messages" && options.inferenceLogPath ? {
|
|
10992
11058
|
logPath: options.inferenceLogPath,
|
|
10993
11059
|
requestId,
|
|
11060
|
+
claudeSessionId,
|
|
10994
11061
|
modelId: typeof parsed?.model === "string" ? parsed.model : "unknown",
|
|
10995
11062
|
provider: "anthropic",
|
|
10996
11063
|
progressIntervalMs: options.responseProgressIntervalMs ?? INFERENCE_PROGRESS_INTERVAL_MS
|
|
10997
|
-
} : void 0
|
|
11064
|
+
} : void 0,
|
|
11065
|
+
() => shuttingDown
|
|
10998
11066
|
);
|
|
10999
11067
|
return;
|
|
11000
11068
|
}
|
|
@@ -11072,6 +11140,7 @@ async function startHttpProxy(options) {
|
|
|
11072
11140
|
inferenceLogPath: options.inferenceLogPath,
|
|
11073
11141
|
webSocketDiagnosticsLogPath: options.webSocketDiagnosticsLogPath,
|
|
11074
11142
|
close: async () => {
|
|
11143
|
+
shuttingDown = true;
|
|
11075
11144
|
for (const socket of sockets) socket.destroy();
|
|
11076
11145
|
await new Promise((resolve2) => proxyServer.close(() => resolve2()));
|
|
11077
11146
|
mitmServer.close();
|
|
@@ -11139,18 +11208,40 @@ function reportSkippedHttpProxyFavorites(loaded) {
|
|
|
11139
11208
|
);
|
|
11140
11209
|
}
|
|
11141
11210
|
}
|
|
11142
|
-
|
|
11143
|
-
|
|
11144
|
-
const handle = await startHttpProxy({
|
|
11211
|
+
function buildConfiguredHttpProxyOptions(loaded, port, debug = false, inferenceLogPath = getInferenceRequestLogPath(), debugLogPath, webSocketDiagnosticsLogPath) {
|
|
11212
|
+
return {
|
|
11145
11213
|
host: "127.0.0.1",
|
|
11146
11214
|
port,
|
|
11147
11215
|
routes: loaded.routes,
|
|
11148
11216
|
modelAliases: loaded.aliases,
|
|
11217
|
+
reservedModelIds: loaded.unavailableAliases.map((alias) => alias.name),
|
|
11149
11218
|
debug,
|
|
11150
11219
|
debugLogPath,
|
|
11151
11220
|
inferenceLogPath,
|
|
11152
11221
|
webSocketDiagnosticsLogPath
|
|
11153
|
-
}
|
|
11222
|
+
};
|
|
11223
|
+
}
|
|
11224
|
+
function formatHttpProxyEnvironmentLines(handle, baseEnv = process.env) {
|
|
11225
|
+
const env = { ...baseEnv };
|
|
11226
|
+
const hadNoProxy = env["NO_PROXY"] !== void 0 || env["no_proxy"] !== void 0;
|
|
11227
|
+
removeAnthropicProxyBypass(env);
|
|
11228
|
+
return [
|
|
11229
|
+
` HTTPS_PROXY=http://127.0.0.1:${handle.port}`,
|
|
11230
|
+
` HTTP_PROXY=http://127.0.0.1:${handle.port}`,
|
|
11231
|
+
` NODE_EXTRA_CA_CERTS=${handle.caCertPath}`,
|
|
11232
|
+
...hadNoProxy ? [` NO_PROXY=${env["NO_PROXY"] ?? ""}`, ` no_proxy=${env["no_proxy"] ?? ""}`] : []
|
|
11233
|
+
];
|
|
11234
|
+
}
|
|
11235
|
+
async function startConfiguredHttpProxy(port, debug = false, inferenceLogPath = getInferenceRequestLogPath(), debugLogPath, webSocketDiagnosticsLogPath) {
|
|
11236
|
+
const loaded = await loadHttpProxyRoutes();
|
|
11237
|
+
const handle = await startHttpProxy(buildConfiguredHttpProxyOptions(
|
|
11238
|
+
loaded,
|
|
11239
|
+
port,
|
|
11240
|
+
debug,
|
|
11241
|
+
inferenceLogPath,
|
|
11242
|
+
debugLogPath,
|
|
11243
|
+
webSocketDiagnosticsLogPath
|
|
11244
|
+
));
|
|
11154
11245
|
handle.caCertPath = ensureHttpProxyCaBundle(
|
|
11155
11246
|
handle.caCertPath,
|
|
11156
11247
|
process.env["NODE_EXTRA_CA_CERTS"]
|
|
@@ -11194,9 +11285,7 @@ async function runHttpProxyServerCommand(debug = false, webSocketDiagnostics = f
|
|
|
11194
11285
|
});
|
|
11195
11286
|
console.log("");
|
|
11196
11287
|
console.log(pc9.bold(pc9.green("clodex proxy-mode server running")));
|
|
11197
|
-
console.log(
|
|
11198
|
-
console.log(` HTTP_PROXY=http://127.0.0.1:${handle.port}`);
|
|
11199
|
-
console.log(` NODE_EXTRA_CA_CERTS=${handle.caCertPath}`);
|
|
11288
|
+
for (const line of formatHttpProxyEnvironmentLines(handle)) console.log(line);
|
|
11200
11289
|
console.log(` Request log: ${handle.inferenceLogPath}`);
|
|
11201
11290
|
if (handle.webSocketDiagnosticsLogPath) {
|
|
11202
11291
|
console.log(` WebSocket diagnostics: ${handle.webSocketDiagnosticsLogPath}`);
|
|
@@ -12824,10 +12913,18 @@ function printHelp(text4) {
|
|
|
12824
12913
|
${text4}
|
|
12825
12914
|
`);
|
|
12826
12915
|
}
|
|
12827
|
-
async function launchClaudeViaCatalog(catalogRoutes, startingRoute, contextWindow, trace, claudeArgs) {
|
|
12916
|
+
async function launchClaudeViaCatalog(catalogRoutes, startingRoute, modelAliases, contextWindow, trace, claudeArgs) {
|
|
12828
12917
|
let proxyHandle;
|
|
12829
12918
|
try {
|
|
12830
|
-
proxyHandle = await startProxyCatalog(
|
|
12919
|
+
proxyHandle = await startProxyCatalog(
|
|
12920
|
+
catalogRoutes,
|
|
12921
|
+
startingRoute.aliasId,
|
|
12922
|
+
trace,
|
|
12923
|
+
void 0,
|
|
12924
|
+
void 0,
|
|
12925
|
+
void 0,
|
|
12926
|
+
modelAliases
|
|
12927
|
+
);
|
|
12831
12928
|
p12.log.info(
|
|
12832
12929
|
`Switch menu active \u2014 proxy on port ${proxyHandle.port} ` + pc13.dim(`(${catalogRoutes.length} model${catalogRoutes.length !== 1 ? "s" : ""} in /model)`)
|
|
12833
12930
|
);
|
|
@@ -13380,6 +13477,7 @@ Error: ${launchPlan.error}
|
|
|
13380
13477
|
return launchClaudeViaCatalog(
|
|
13381
13478
|
catalogRoutes,
|
|
13382
13479
|
startingRoute,
|
|
13480
|
+
resolveCatalogModelAliases(prefs.modelAliases ?? [], resolveRoute),
|
|
13383
13481
|
selectedModel.contextWindow,
|
|
13384
13482
|
trace,
|
|
13385
13483
|
claudeArgs
|