clawmux 0.3.0 → 0.3.2
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 +0 -13
- package/dist/cli.cjs +3768 -112
- package/dist/index.cjs +235 -34
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -1,4 +1,27 @@
|
|
|
1
1
|
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
5
|
+
function __accessProp(key) {
|
|
6
|
+
return this[key];
|
|
7
|
+
}
|
|
8
|
+
var __toCommonJS = (from) => {
|
|
9
|
+
var entry = (__moduleCache ??= new WeakMap).get(from), desc;
|
|
10
|
+
if (entry)
|
|
11
|
+
return entry;
|
|
12
|
+
entry = __defProp({}, "__esModule", { value: true });
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (var key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(entry, key))
|
|
16
|
+
__defProp(entry, key, {
|
|
17
|
+
get: __accessProp.bind(from, key),
|
|
18
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
__moduleCache.set(from, entry);
|
|
22
|
+
return entry;
|
|
23
|
+
};
|
|
24
|
+
var __moduleCache;
|
|
2
25
|
var __returnValue = (v) => v;
|
|
3
26
|
function __exportSetter(name, newValue) {
|
|
4
27
|
this[name] = __returnValue.bind(null, newValue);
|
|
@@ -77,6 +100,13 @@ async function writeWebResponse(res, response) {
|
|
|
77
100
|
}
|
|
78
101
|
}
|
|
79
102
|
|
|
103
|
+
// src/index.ts
|
|
104
|
+
var exports_src = {};
|
|
105
|
+
__export(exports_src, {
|
|
106
|
+
bootstrap: () => bootstrap
|
|
107
|
+
});
|
|
108
|
+
module.exports = __toCommonJS(exports_src);
|
|
109
|
+
|
|
80
110
|
// src/proxy/router.ts
|
|
81
111
|
var VERSION = "0.1.0";
|
|
82
112
|
function jsonResponse(body, status = 200) {
|
|
@@ -579,14 +609,7 @@ function getAuthProfilesPath(agentId) {
|
|
|
579
609
|
const id = agentId ?? "main";
|
|
580
610
|
return import_node_path2.join(getHomeDir(), ".openclaw", "agents", id, "agent", "auth-profiles.json");
|
|
581
611
|
}
|
|
582
|
-
|
|
583
|
-
const path = profilesPath ?? getAuthProfilesPath(agentId);
|
|
584
|
-
let text;
|
|
585
|
-
try {
|
|
586
|
-
text = await import_promises4.readFile(path, "utf-8");
|
|
587
|
-
} catch {
|
|
588
|
-
return [];
|
|
589
|
-
}
|
|
612
|
+
function parseAuthProfilesFile(text) {
|
|
590
613
|
try {
|
|
591
614
|
const parsed = JSON.parse(text);
|
|
592
615
|
if (Array.isArray(parsed))
|
|
@@ -594,7 +617,7 @@ async function readAuthProfiles(agentId, profilesPath) {
|
|
|
594
617
|
if (parsed && typeof parsed === "object" && parsed.profiles) {
|
|
595
618
|
return Object.entries(parsed.profiles).map(([key, profile]) => ({
|
|
596
619
|
provider: profile.provider ?? key.split(":")[0],
|
|
597
|
-
apiKey: profile.access ?? profile.apiKey,
|
|
620
|
+
apiKey: profile.access ?? profile.apiKey ?? profile.key,
|
|
598
621
|
token: profile.token
|
|
599
622
|
})).filter((p) => {
|
|
600
623
|
const token = p.apiKey ?? p.token;
|
|
@@ -617,6 +640,33 @@ async function readAuthProfiles(agentId, profilesPath) {
|
|
|
617
640
|
throw new Error(`Failed to parse auth-profiles.json: ${message}`);
|
|
618
641
|
}
|
|
619
642
|
}
|
|
643
|
+
async function readAuthProfiles(_agentId, _profilesPath, agentsDirOverride) {
|
|
644
|
+
const agentsDir = agentsDirOverride ?? import_node_path2.join(getHomeDir(), ".openclaw", "agents");
|
|
645
|
+
let agentDirs;
|
|
646
|
+
try {
|
|
647
|
+
agentDirs = (await import_promises4.readdir(agentsDir, { withFileTypes: true })).filter((d) => d.isDirectory()).map((d) => d.name).sort();
|
|
648
|
+
} catch {
|
|
649
|
+
const path = getAuthProfilesPath(_agentId);
|
|
650
|
+
try {
|
|
651
|
+
return parseAuthProfilesFile(await import_promises4.readFile(path, "utf-8"));
|
|
652
|
+
} catch {
|
|
653
|
+
return [];
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
const ordered = ["main", ...agentDirs.filter((d) => d !== "main")];
|
|
657
|
+
const merged = new Map;
|
|
658
|
+
for (const agentId of ordered) {
|
|
659
|
+
const profilePath = import_node_path2.join(agentsDir, agentId, "agent", "auth-profiles.json");
|
|
660
|
+
try {
|
|
661
|
+
const text = await import_promises4.readFile(profilePath, "utf-8");
|
|
662
|
+
const profiles = parseAuthProfilesFile(text);
|
|
663
|
+
for (const p of profiles) {
|
|
664
|
+
merged.set(p.provider, p);
|
|
665
|
+
}
|
|
666
|
+
} catch {}
|
|
667
|
+
}
|
|
668
|
+
return Array.from(merged.values());
|
|
669
|
+
}
|
|
620
670
|
function getProviderConfig(provider, config) {
|
|
621
671
|
return config.models?.providers?.[provider];
|
|
622
672
|
}
|
|
@@ -765,6 +815,38 @@ function getAdapter(apiType) {
|
|
|
765
815
|
return adapters.get(apiType);
|
|
766
816
|
}
|
|
767
817
|
|
|
818
|
+
// src/adapters/tool-converter.ts
|
|
819
|
+
function toOpenAITools(tools) {
|
|
820
|
+
if (!Array.isArray(tools) || tools.length === 0)
|
|
821
|
+
return;
|
|
822
|
+
return tools.map((tool) => {
|
|
823
|
+
if (tool.type === "function" && tool.function)
|
|
824
|
+
return tool;
|
|
825
|
+
return {
|
|
826
|
+
type: "function",
|
|
827
|
+
function: {
|
|
828
|
+
name: tool.name ?? "",
|
|
829
|
+
description: tool.description ?? "",
|
|
830
|
+
parameters: tool.input_schema ?? tool.parameters ?? { type: "object", properties: {} }
|
|
831
|
+
}
|
|
832
|
+
};
|
|
833
|
+
});
|
|
834
|
+
}
|
|
835
|
+
function toAnthropicTools(tools) {
|
|
836
|
+
if (!Array.isArray(tools) || tools.length === 0)
|
|
837
|
+
return;
|
|
838
|
+
return tools.map((tool) => {
|
|
839
|
+
if (tool.input_schema && !tool.type)
|
|
840
|
+
return tool;
|
|
841
|
+
const fn = tool.function ?? tool;
|
|
842
|
+
return {
|
|
843
|
+
name: fn.name ?? "",
|
|
844
|
+
description: fn.description ?? "",
|
|
845
|
+
input_schema: fn.parameters ?? { type: "object", properties: {} }
|
|
846
|
+
};
|
|
847
|
+
});
|
|
848
|
+
}
|
|
849
|
+
|
|
768
850
|
// src/adapters/anthropic.ts
|
|
769
851
|
class AnthropicAdapter {
|
|
770
852
|
apiType = "anthropic-messages";
|
|
@@ -791,18 +873,42 @@ class AnthropicAdapter {
|
|
|
791
873
|
"anthropic-version": "2023-06-01",
|
|
792
874
|
"content-type": "application/json"
|
|
793
875
|
};
|
|
794
|
-
let bodyObj = {
|
|
795
|
-
...parsed.rawBody,
|
|
796
|
-
model: targetModel
|
|
797
|
-
};
|
|
798
876
|
const isHaiku = targetModel.toLowerCase().includes("haiku");
|
|
799
877
|
const hasThinking = "thinking" in parsed.rawBody;
|
|
800
878
|
if (hasThinking && !isHaiku) {
|
|
801
879
|
headers["anthropic-beta"] = "interleaved-thinking-2025-05-14";
|
|
802
880
|
}
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
881
|
+
const ANTHROPIC_SAMPLING_KEYS = [
|
|
882
|
+
"temperature",
|
|
883
|
+
"top_p",
|
|
884
|
+
"top_k",
|
|
885
|
+
"stop_sequences",
|
|
886
|
+
"metadata",
|
|
887
|
+
"service_tier"
|
|
888
|
+
];
|
|
889
|
+
const samplingParams = {};
|
|
890
|
+
for (const key of ANTHROPIC_SAMPLING_KEYS) {
|
|
891
|
+
if (key in parsed.rawBody) {
|
|
892
|
+
samplingParams[key] = parsed.rawBody[key];
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
let bodyObj = {
|
|
896
|
+
model: targetModel,
|
|
897
|
+
messages: parsed.messages,
|
|
898
|
+
stream: parsed.stream,
|
|
899
|
+
...samplingParams
|
|
900
|
+
};
|
|
901
|
+
if (parsed.system !== undefined) {
|
|
902
|
+
bodyObj.system = parsed.system;
|
|
903
|
+
}
|
|
904
|
+
if (parsed.maxTokens !== undefined) {
|
|
905
|
+
bodyObj.max_tokens = parsed.maxTokens;
|
|
906
|
+
}
|
|
907
|
+
if (parsed.rawBody.tools) {
|
|
908
|
+
bodyObj.tools = toAnthropicTools(parsed.rawBody.tools);
|
|
909
|
+
}
|
|
910
|
+
if (!isHaiku && hasThinking) {
|
|
911
|
+
bodyObj.thinking = parsed.rawBody.thinking;
|
|
806
912
|
}
|
|
807
913
|
return {
|
|
808
914
|
url,
|
|
@@ -1069,11 +1175,43 @@ class OpenAICompletionsAdapter {
|
|
|
1069
1175
|
return parseOpenAIBody(body);
|
|
1070
1176
|
}
|
|
1071
1177
|
buildUpstreamRequest(parsed, targetModel, baseUrl, auth) {
|
|
1072
|
-
const
|
|
1178
|
+
const messages = [];
|
|
1179
|
+
if (parsed.system !== undefined) {
|
|
1180
|
+
messages.push({ role: "system", content: parsed.system });
|
|
1181
|
+
}
|
|
1182
|
+
messages.push(...parsed.messages);
|
|
1183
|
+
const OPENAI_SAMPLING_KEYS = [
|
|
1184
|
+
"temperature",
|
|
1185
|
+
"top_p",
|
|
1186
|
+
"frequency_penalty",
|
|
1187
|
+
"presence_penalty",
|
|
1188
|
+
"logprobs",
|
|
1189
|
+
"top_logprobs",
|
|
1190
|
+
"seed",
|
|
1191
|
+
"stop",
|
|
1192
|
+
"n",
|
|
1193
|
+
"logit_bias",
|
|
1194
|
+
"response_format",
|
|
1195
|
+
"reasoning_effort"
|
|
1196
|
+
];
|
|
1197
|
+
const samplingParams = {};
|
|
1198
|
+
for (const key of OPENAI_SAMPLING_KEYS) {
|
|
1199
|
+
if (key in parsed.rawBody) {
|
|
1200
|
+
samplingParams[key] = parsed.rawBody[key];
|
|
1201
|
+
}
|
|
1202
|
+
}
|
|
1073
1203
|
const upstreamBody = {
|
|
1074
|
-
|
|
1075
|
-
|
|
1204
|
+
model: targetModel,
|
|
1205
|
+
messages,
|
|
1206
|
+
stream: parsed.stream,
|
|
1207
|
+
...samplingParams
|
|
1076
1208
|
};
|
|
1209
|
+
if (parsed.maxTokens !== undefined) {
|
|
1210
|
+
upstreamBody.max_tokens = parsed.maxTokens;
|
|
1211
|
+
}
|
|
1212
|
+
if (parsed.rawBody.tools) {
|
|
1213
|
+
upstreamBody.tools = toOpenAITools(parsed.rawBody.tools);
|
|
1214
|
+
}
|
|
1077
1215
|
return {
|
|
1078
1216
|
url: /\/v\d+\/?$/.test(baseUrl) ? `${baseUrl.replace(/\/$/, "")}/chat/completions` : `${baseUrl}/v1/chat/completions`,
|
|
1079
1217
|
method: "POST",
|
|
@@ -1100,8 +1238,11 @@ class OpenAICompletionsAdapter {
|
|
|
1100
1238
|
if (Array.isArray(choices) && choices.length > 0) {
|
|
1101
1239
|
const choice = choices[0];
|
|
1102
1240
|
const message = choice.message;
|
|
1103
|
-
if (message
|
|
1104
|
-
|
|
1241
|
+
if (message) {
|
|
1242
|
+
const messageText = message.content ?? message.reasoning_content;
|
|
1243
|
+
if (typeof messageText === "string") {
|
|
1244
|
+
content = messageText;
|
|
1245
|
+
}
|
|
1105
1246
|
}
|
|
1106
1247
|
if (typeof choice.finish_reason === "string") {
|
|
1107
1248
|
stopReason = choice.finish_reason;
|
|
@@ -1171,16 +1312,17 @@ class OpenAICompletionsAdapter {
|
|
|
1171
1312
|
const choice = choices[0];
|
|
1172
1313
|
const delta = choice.delta;
|
|
1173
1314
|
const finishReason = choice.finish_reason;
|
|
1174
|
-
|
|
1315
|
+
const textContent = delta?.content ?? delta?.reasoning_content;
|
|
1316
|
+
if (delta?.role === "assistant" && textContent == null) {
|
|
1175
1317
|
events.push({
|
|
1176
1318
|
type: "message_start",
|
|
1177
1319
|
id: String(data.id ?? ""),
|
|
1178
1320
|
model: String(data.model ?? "")
|
|
1179
1321
|
});
|
|
1180
|
-
} else if (typeof
|
|
1322
|
+
} else if (typeof textContent === "string") {
|
|
1181
1323
|
events.push({
|
|
1182
1324
|
type: "content_delta",
|
|
1183
|
-
text:
|
|
1325
|
+
text: textContent,
|
|
1184
1326
|
index: typeof choice.index === "number" ? choice.index : 0
|
|
1185
1327
|
});
|
|
1186
1328
|
}
|
|
@@ -1212,7 +1354,7 @@ class OpenAICompletionsAdapter {
|
|
|
1212
1354
|
choices: [
|
|
1213
1355
|
{
|
|
1214
1356
|
index: 0,
|
|
1215
|
-
delta: { role: "assistant"
|
|
1357
|
+
delta: { role: "assistant" },
|
|
1216
1358
|
finish_reason: null
|
|
1217
1359
|
}
|
|
1218
1360
|
]
|
|
@@ -1272,11 +1414,40 @@ class OpenAIResponsesAdapter {
|
|
|
1272
1414
|
return parseOpenAIBody(body);
|
|
1273
1415
|
}
|
|
1274
1416
|
buildUpstreamRequest(parsed, targetModel, baseUrl, auth) {
|
|
1275
|
-
const
|
|
1417
|
+
const input = [];
|
|
1418
|
+
if (parsed.system !== undefined) {
|
|
1419
|
+
input.push({ role: "system", content: parsed.system });
|
|
1420
|
+
}
|
|
1421
|
+
input.push(...parsed.messages);
|
|
1422
|
+
const OPENAI_RESPONSES_SAMPLING_KEYS = [
|
|
1423
|
+
"temperature",
|
|
1424
|
+
"top_p",
|
|
1425
|
+
"truncation",
|
|
1426
|
+
"reasoning",
|
|
1427
|
+
"reasoning_effort",
|
|
1428
|
+
"text",
|
|
1429
|
+
"metadata",
|
|
1430
|
+
"store",
|
|
1431
|
+
"include"
|
|
1432
|
+
];
|
|
1433
|
+
const samplingParams = {};
|
|
1434
|
+
for (const key of OPENAI_RESPONSES_SAMPLING_KEYS) {
|
|
1435
|
+
if (key in parsed.rawBody) {
|
|
1436
|
+
samplingParams[key] = parsed.rawBody[key];
|
|
1437
|
+
}
|
|
1438
|
+
}
|
|
1276
1439
|
const upstreamBody = {
|
|
1277
|
-
|
|
1278
|
-
|
|
1440
|
+
model: targetModel,
|
|
1441
|
+
input,
|
|
1442
|
+
stream: parsed.stream,
|
|
1443
|
+
...samplingParams
|
|
1279
1444
|
};
|
|
1445
|
+
if (parsed.maxTokens !== undefined) {
|
|
1446
|
+
upstreamBody.max_output_tokens = parsed.maxTokens;
|
|
1447
|
+
}
|
|
1448
|
+
if (parsed.rawBody.tools) {
|
|
1449
|
+
upstreamBody.tools = toOpenAITools(parsed.rawBody.tools);
|
|
1450
|
+
}
|
|
1280
1451
|
return {
|
|
1281
1452
|
url: `${baseUrl}/v1/responses`,
|
|
1282
1453
|
method: "POST",
|
|
@@ -2285,6 +2456,9 @@ class OpenAICodexAdapter {
|
|
|
2285
2456
|
}
|
|
2286
2457
|
delete upstreamBody.max_tokens;
|
|
2287
2458
|
delete upstreamBody.max_output_tokens;
|
|
2459
|
+
if (upstreamBody.tools) {
|
|
2460
|
+
upstreamBody.tools = toOpenAITools(upstreamBody.tools);
|
|
2461
|
+
}
|
|
2288
2462
|
return {
|
|
2289
2463
|
url: `${baseUrl}/codex/responses`,
|
|
2290
2464
|
method: "POST",
|
|
@@ -2941,6 +3115,7 @@ function createStreamTranslator(sourceAdapter, targetAdapter) {
|
|
|
2941
3115
|
return new TransformStream;
|
|
2942
3116
|
}
|
|
2943
3117
|
let buffer = "";
|
|
3118
|
+
let messageStarted = false;
|
|
2944
3119
|
return new TransformStream({
|
|
2945
3120
|
transform(chunk, controller) {
|
|
2946
3121
|
if (!sourceAdapter.parseStreamChunk || !targetAdapter.buildStreamChunk) {
|
|
@@ -2958,6 +3133,18 @@ function createStreamTranslator(sourceAdapter, targetAdapter) {
|
|
|
2958
3133
|
continue;
|
|
2959
3134
|
const events = sourceAdapter.parseStreamChunk(frame);
|
|
2960
3135
|
for (const event of events) {
|
|
3136
|
+
if (event.type === "message_start") {
|
|
3137
|
+
messageStarted = true;
|
|
3138
|
+
} else if (!messageStarted && (event.type === "content_delta" || event.type === "content_stop")) {
|
|
3139
|
+
messageStarted = true;
|
|
3140
|
+
const synthetic = targetAdapter.buildStreamChunk({
|
|
3141
|
+
type: "message_start",
|
|
3142
|
+
id: "",
|
|
3143
|
+
model: ""
|
|
3144
|
+
});
|
|
3145
|
+
if (synthetic)
|
|
3146
|
+
controller.enqueue(encoder.encode(synthetic));
|
|
3147
|
+
}
|
|
2961
3148
|
const translated = targetAdapter.buildStreamChunk(event);
|
|
2962
3149
|
controller.enqueue(encoder.encode(translated));
|
|
2963
3150
|
}
|
|
@@ -2967,6 +3154,18 @@ function createStreamTranslator(sourceAdapter, targetAdapter) {
|
|
|
2967
3154
|
if (buffer.trim() !== "" && sourceAdapter.parseStreamChunk && targetAdapter.buildStreamChunk) {
|
|
2968
3155
|
const events = sourceAdapter.parseStreamChunk(buffer);
|
|
2969
3156
|
for (const event of events) {
|
|
3157
|
+
if (event.type === "message_start") {
|
|
3158
|
+
messageStarted = true;
|
|
3159
|
+
} else if (!messageStarted && (event.type === "content_delta" || event.type === "content_stop")) {
|
|
3160
|
+
messageStarted = true;
|
|
3161
|
+
const synthetic = targetAdapter.buildStreamChunk({
|
|
3162
|
+
type: "message_start",
|
|
3163
|
+
id: "",
|
|
3164
|
+
model: ""
|
|
3165
|
+
});
|
|
3166
|
+
if (synthetic)
|
|
3167
|
+
controller.enqueue(encoder.encode(synthetic));
|
|
3168
|
+
}
|
|
2970
3169
|
const translated = targetAdapter.buildStreamChunk(event);
|
|
2971
3170
|
controller.enqueue(encoder.encode(translated));
|
|
2972
3171
|
}
|
|
@@ -3658,7 +3857,7 @@ function setupPipelineRoutes(config, openclawConfig, authProfiles, compressionMi
|
|
|
3658
3857
|
|
|
3659
3858
|
// src/index.ts
|
|
3660
3859
|
var import_node_path4 = require("node:path");
|
|
3661
|
-
async function bootstrap() {
|
|
3860
|
+
async function bootstrap(portOverride) {
|
|
3662
3861
|
const configPath = process.env.CLAWMUX_CONFIG ? import_node_path4.resolve(process.env.CLAWMUX_CONFIG) : import_node_path4.resolve(process.cwd(), "clawmux.json");
|
|
3663
3862
|
const result = await loadConfig(configPath);
|
|
3664
3863
|
if (!result.valid) {
|
|
@@ -3673,7 +3872,7 @@ async function bootstrap() {
|
|
|
3673
3872
|
const piAiCatalog = await loadPiAiCatalog();
|
|
3674
3873
|
const compressionMiddleware = createResolvedCompressionMiddleware(config, openclawConfig, piAiCatalog);
|
|
3675
3874
|
setupPipelineRoutes(config, openclawConfig, authProfiles, compressionMiddleware);
|
|
3676
|
-
const port = parseInt(process.env.CLAWMUX_PORT ?? "3456", 10);
|
|
3875
|
+
const port = portOverride ?? parseInt(process.env.CLAWMUX_PORT ?? "3456", 10);
|
|
3677
3876
|
const server = createServer({ port, host: "127.0.0.1" });
|
|
3678
3877
|
server.start();
|
|
3679
3878
|
console.log(`[clawmux] Proxy server running on http://127.0.0.1:${port}`);
|
|
@@ -3685,7 +3884,9 @@ async function bootstrap() {
|
|
|
3685
3884
|
});
|
|
3686
3885
|
watcher.start();
|
|
3687
3886
|
}
|
|
3688
|
-
|
|
3689
|
-
|
|
3690
|
-
|
|
3691
|
-
|
|
3887
|
+
if (require.main == module || typeof Bun !== "undefined" && Bun.main === "/home/runner/work/ClawMux/ClawMux/src/index.ts") {
|
|
3888
|
+
bootstrap().catch((err) => {
|
|
3889
|
+
console.error(`[clawmux] Fatal: ${err.message}`);
|
|
3890
|
+
process.exit(1);
|
|
3891
|
+
});
|
|
3892
|
+
}
|