@musistudio/claude-code-router 1.0.44 → 1.0.46
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 -0
- package/README_zh.md +1 -0
- package/dist/cli.js +137 -101
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -570,6 +570,7 @@ A huge thank you to all our sponsors for their generous support!
|
|
|
570
570
|
- @zcutlip
|
|
571
571
|
- [@Peng-YM](http://github.com/Peng-YM)
|
|
572
572
|
- @\*更
|
|
573
|
+
- @\*.
|
|
573
574
|
|
|
574
575
|
|
|
575
576
|
(If your name is masked, please contact me via my homepage email to update it with your GitHub username.)
|
package/README_zh.md
CHANGED
package/dist/cli.js
CHANGED
|
@@ -66532,7 +66532,7 @@ var require_package3 = __commonJS({
|
|
|
66532
66532
|
"package.json"(exports2, module2) {
|
|
66533
66533
|
module2.exports = {
|
|
66534
66534
|
name: "@musistudio/claude-code-router",
|
|
66535
|
-
version: "1.0.
|
|
66535
|
+
version: "1.0.46",
|
|
66536
66536
|
description: "Use Claude Code without an Anthropics account and route it to another LLM provider",
|
|
66537
66537
|
bin: {
|
|
66538
66538
|
ccr: "./dist/cli.js"
|
|
@@ -66552,7 +66552,7 @@ var require_package3 = __commonJS({
|
|
|
66552
66552
|
license: "MIT",
|
|
66553
66553
|
dependencies: {
|
|
66554
66554
|
"@fastify/static": "^8.2.0",
|
|
66555
|
-
"@musistudio/llms": "^1.0.
|
|
66555
|
+
"@musistudio/llms": "^1.0.30",
|
|
66556
66556
|
dotenv: "^16.4.7",
|
|
66557
66557
|
json5: "^2.2.3",
|
|
66558
66558
|
openurl: "^1.1.1",
|
|
@@ -67044,7 +67044,7 @@ var import_os = require("os");
|
|
|
67044
67044
|
var import_path4 = require("path");
|
|
67045
67045
|
init_utils();
|
|
67046
67046
|
|
|
67047
|
-
// node_modules/.pnpm/@musistudio+llms@1.0.
|
|
67047
|
+
// node_modules/.pnpm/@musistudio+llms@1.0.30_ws@8.18.3/node_modules/@musistudio/llms/dist/esm/server.mjs
|
|
67048
67048
|
var import_node_fs = require("node:fs");
|
|
67049
67049
|
var import_node_path4 = require("node:path");
|
|
67050
67050
|
var import_node_stream = __toESM(require("node:stream"), 1);
|
|
@@ -76071,7 +76071,7 @@ function Uf(r, e, t, n) {
|
|
|
76071
76071
|
let l = { method: "POST", headers: o, body: JSON.stringify(e), signal: a };
|
|
76072
76072
|
return t.httpsProxy && (l.dispatcher = new import_undici.ProxyAgent(new URL(t.httpsProxy).toString())), n?.debug({ request: l, headers: Object.fromEntries(o.entries()), requestUrl: typeof r == "string" ? r : r.toString(), useProxy: t.httpsProxy }, "final request"), fetch(typeof r == "string" ? r : r.toString(), l);
|
|
76073
76073
|
}
|
|
76074
|
-
var Mf = "1.0.
|
|
76074
|
+
var Mf = "1.0.30";
|
|
76075
76075
|
async function a0(r, e, t, n) {
|
|
76076
76076
|
let o = r.body, a = r.provider, u = t._server.providerService.getProvider(a);
|
|
76077
76077
|
if (!u) throw nt(`Provider '${a}' not found`, 404, "provider_not_found");
|
|
@@ -76372,7 +76372,7 @@ var bo = class {
|
|
|
76372
76372
|
let f = a.content.filter((h) => h.type === "text" && h.text || h.type === "image" && h.source);
|
|
76373
76373
|
f.length && t.push({ role: "user", content: f.map((h) => h?.type === "image" ? { type: "image_url", image_url: { url: h.source?.type === "base64" ? `data:${h.source.media_type},${h.source.data}` : h.source.url }, media_type: h.source.media_type } : h) });
|
|
76374
76374
|
} else if (a.role === "assistant") {
|
|
76375
|
-
let l = { role: "assistant", content:
|
|
76375
|
+
let l = { role: "assistant", content: "" }, f = a.content.filter((d) => d.type === "text" && d.text);
|
|
76376
76376
|
f.length && (l.content = f.map((d) => d.text).join(`
|
|
76377
76377
|
`));
|
|
76378
76378
|
let h = a.content.filter((d) => d.type === "tool_use" && d.id);
|
|
@@ -79320,9 +79320,6 @@ var rewriteStream = (stream, processor) => {
|
|
|
79320
79320
|
// src/index.ts
|
|
79321
79321
|
var import_json52 = __toESM(require_lib());
|
|
79322
79322
|
|
|
79323
|
-
// src/agents/image.agent.ts
|
|
79324
|
-
var import_crypto3 = require("crypto");
|
|
79325
|
-
|
|
79326
79323
|
// node_modules/.pnpm/lru-cache@11.1.0/node_modules/lru-cache/dist/esm/index.js
|
|
79327
79324
|
var perf = typeof performance === "object" && performance && typeof performance.now === "function" ? performance : Date;
|
|
79328
79325
|
var warned = /* @__PURE__ */ new Set();
|
|
@@ -80719,14 +80716,8 @@ var ImageCache = class {
|
|
|
80719
80716
|
ttl: 24 * 60 * 60 * 1e3
|
|
80720
80717
|
});
|
|
80721
80718
|
}
|
|
80722
|
-
calculateHash(base64Image) {
|
|
80723
|
-
const hash = (0, import_crypto3.createHash)("sha256");
|
|
80724
|
-
hash.update(base64Image);
|
|
80725
|
-
return hash.digest("hex");
|
|
80726
|
-
}
|
|
80727
80719
|
storeImage(id2, source) {
|
|
80728
80720
|
if (this.hasImage(id2)) return;
|
|
80729
|
-
const base64Image = source.data;
|
|
80730
80721
|
this.cache.set(id2, {
|
|
80731
80722
|
source,
|
|
80732
80723
|
timestamp: Date.now()
|
|
@@ -80755,7 +80746,7 @@ var ImageAgent = class {
|
|
|
80755
80746
|
this.appendTools();
|
|
80756
80747
|
}
|
|
80757
80748
|
shouldHandle(req, config) {
|
|
80758
|
-
if (!config.Router.image) return false;
|
|
80749
|
+
if (!config.Router.image || req.body.model === config.Router.image) return false;
|
|
80759
80750
|
const lastMessage = req.body.messages[req.body.messages.length - 1];
|
|
80760
80751
|
if (!config.forceUseImageAgent && lastMessage.role === "user" && Array.isArray(lastMessage.content) && lastMessage.content.find((item) => item.type === "image")) {
|
|
80761
80752
|
req.body.model = config.Router.image;
|
|
@@ -80801,6 +80792,7 @@ var ImageAgent = class {
|
|
|
80801
80792
|
"required": ["imageId", "task"]
|
|
80802
80793
|
},
|
|
80803
80794
|
handler: async (args, context) => {
|
|
80795
|
+
console.log("args", JSON.stringify(args, null, 2));
|
|
80804
80796
|
const imageMessages = [];
|
|
80805
80797
|
let imageId;
|
|
80806
80798
|
if (args.imageId && Array.isArray(args.imageId)) {
|
|
@@ -80832,7 +80824,10 @@ var ImageAgent = class {
|
|
|
80832
80824
|
model: context.config.Router.image,
|
|
80833
80825
|
system: [{
|
|
80834
80826
|
type: "text",
|
|
80835
|
-
text:
|
|
80827
|
+
text: `You must interpret and analyze images strictly according to the assigned task.
|
|
80828
|
+
When an image placeholder is provided, your role is to parse the image content only within the scope of the user\u2019s instructions.
|
|
80829
|
+
Do not ignore or deviate from the task.
|
|
80830
|
+
Always ensure that your response reflects a clear, accurate interpretation of the image aligned with the given objective.`
|
|
80836
80831
|
}],
|
|
80837
80832
|
messages: [
|
|
80838
80833
|
{
|
|
@@ -80845,6 +80840,7 @@ var ImageAgent = class {
|
|
|
80845
80840
|
}).then((res) => res.json()).catch((err) => {
|
|
80846
80841
|
return null;
|
|
80847
80842
|
});
|
|
80843
|
+
console.log(agentResponse.content);
|
|
80848
80844
|
if (!agentResponse || !agentResponse.content) {
|
|
80849
80845
|
return "analyzeImage Error";
|
|
80850
80846
|
}
|
|
@@ -81018,6 +81014,9 @@ async function run(options = {}) {
|
|
|
81018
81014
|
useAgents.push(agent.name);
|
|
81019
81015
|
agent.reqHandler(req, config);
|
|
81020
81016
|
if (agent.tools.size) {
|
|
81017
|
+
if (!req.body?.tools?.length) {
|
|
81018
|
+
req.body.tools = [];
|
|
81019
|
+
}
|
|
81021
81020
|
req.body.tools.unshift(...Array.from(agent.tools.values()).map((item) => {
|
|
81022
81021
|
return {
|
|
81023
81022
|
name: item.name,
|
|
@@ -81034,10 +81033,11 @@ async function run(options = {}) {
|
|
|
81034
81033
|
await router(req, reply, config);
|
|
81035
81034
|
}
|
|
81036
81035
|
});
|
|
81037
|
-
server.addHook("onSend",
|
|
81036
|
+
server.addHook("onSend", (req, reply, payload, done) => {
|
|
81038
81037
|
if (req.sessionId && req.url.startsWith("/v1/messages")) {
|
|
81039
81038
|
if (payload instanceof ReadableStream) {
|
|
81040
81039
|
if (req.agents) {
|
|
81040
|
+
const abortController = new AbortController();
|
|
81041
81041
|
const eventStream = payload.pipeThrough(new SSEParserTransform());
|
|
81042
81042
|
let currentAgent;
|
|
81043
81043
|
let currentToolIndex = -1;
|
|
@@ -81046,110 +81046,147 @@ async function run(options = {}) {
|
|
|
81046
81046
|
let currentToolId = "";
|
|
81047
81047
|
const toolMessages = [];
|
|
81048
81048
|
const assistantMessages = [];
|
|
81049
|
-
return rewriteStream(eventStream, async (data, controller) => {
|
|
81050
|
-
|
|
81051
|
-
|
|
81052
|
-
|
|
81053
|
-
|
|
81054
|
-
|
|
81055
|
-
|
|
81056
|
-
|
|
81049
|
+
return done(null, rewriteStream(eventStream, async (data, controller) => {
|
|
81050
|
+
try {
|
|
81051
|
+
if (data.event === "content_block_start" && data?.data?.content_block?.name) {
|
|
81052
|
+
const agent = req.agents.find((name) => agents_default.getAgent(name)?.tools.get(data.data.content_block.name));
|
|
81053
|
+
if (agent) {
|
|
81054
|
+
currentAgent = agents_default.getAgent(agent);
|
|
81055
|
+
currentToolIndex = data.data.index;
|
|
81056
|
+
currentToolName = data.data.content_block.name;
|
|
81057
|
+
currentToolId = data.data.content_block.id;
|
|
81058
|
+
return void 0;
|
|
81059
|
+
}
|
|
81060
|
+
}
|
|
81061
|
+
if (currentToolIndex > -1 && data.data.index === currentToolIndex && data.data?.delta?.type === "input_json_delta") {
|
|
81062
|
+
currentToolArgs += data.data?.delta?.partial_json;
|
|
81057
81063
|
return void 0;
|
|
81058
81064
|
}
|
|
81059
|
-
|
|
81060
|
-
|
|
81061
|
-
|
|
81062
|
-
|
|
81063
|
-
|
|
81064
|
-
|
|
81065
|
-
|
|
81066
|
-
|
|
81067
|
-
|
|
81068
|
-
|
|
81069
|
-
|
|
81070
|
-
|
|
81071
|
-
|
|
81065
|
+
if (currentToolIndex > -1 && data.data.index === currentToolIndex && data.data.type === "content_block_stop") {
|
|
81066
|
+
try {
|
|
81067
|
+
const args = import_json52.default.parse(currentToolArgs);
|
|
81068
|
+
assistantMessages.push({
|
|
81069
|
+
type: "tool_use",
|
|
81070
|
+
id: currentToolId,
|
|
81071
|
+
name: currentToolName,
|
|
81072
|
+
input: args
|
|
81073
|
+
});
|
|
81074
|
+
const toolResult = await currentAgent?.tools.get(currentToolName)?.handler(args, {
|
|
81075
|
+
req,
|
|
81076
|
+
config
|
|
81077
|
+
});
|
|
81078
|
+
console.log("result", toolResult);
|
|
81079
|
+
toolMessages.push({
|
|
81080
|
+
"tool_use_id": currentToolId,
|
|
81081
|
+
"type": "tool_result",
|
|
81082
|
+
"content": toolResult
|
|
81083
|
+
});
|
|
81084
|
+
currentAgent = void 0;
|
|
81085
|
+
currentToolIndex = -1;
|
|
81086
|
+
currentToolName = "";
|
|
81087
|
+
currentToolArgs = "";
|
|
81088
|
+
currentToolId = "";
|
|
81089
|
+
} catch (e) {
|
|
81090
|
+
console.log(e);
|
|
81091
|
+
}
|
|
81092
|
+
return void 0;
|
|
81093
|
+
}
|
|
81094
|
+
if (data.event === "message_delta" && toolMessages.length) {
|
|
81095
|
+
req.body.messages.push({
|
|
81096
|
+
role: "assistant",
|
|
81097
|
+
content: assistantMessages
|
|
81072
81098
|
});
|
|
81073
|
-
|
|
81074
|
-
|
|
81075
|
-
|
|
81099
|
+
req.body.messages.push({
|
|
81100
|
+
role: "user",
|
|
81101
|
+
content: toolMessages
|
|
81076
81102
|
});
|
|
81077
|
-
|
|
81078
|
-
"
|
|
81079
|
-
|
|
81080
|
-
|
|
81103
|
+
const response = await fetch(`http://127.0.0.1:${config.PORT}/v1/messages`, {
|
|
81104
|
+
method: "POST",
|
|
81105
|
+
headers: {
|
|
81106
|
+
"x-api-key": config.APIKEY,
|
|
81107
|
+
"content-type": "application/json"
|
|
81108
|
+
},
|
|
81109
|
+
body: JSON.stringify(req.body)
|
|
81081
81110
|
});
|
|
81082
|
-
|
|
81083
|
-
|
|
81084
|
-
currentToolName = "";
|
|
81085
|
-
currentToolArgs = "";
|
|
81086
|
-
currentToolId = "";
|
|
81087
|
-
} catch (e) {
|
|
81088
|
-
console.log(e);
|
|
81089
|
-
}
|
|
81090
|
-
return void 0;
|
|
81091
|
-
}
|
|
81092
|
-
if (data.event === "message_delta" && toolMessages.length) {
|
|
81093
|
-
req.body.messages.push({
|
|
81094
|
-
role: "assistant",
|
|
81095
|
-
content: assistantMessages
|
|
81096
|
-
});
|
|
81097
|
-
req.body.messages.push({
|
|
81098
|
-
role: "user",
|
|
81099
|
-
content: toolMessages
|
|
81100
|
-
});
|
|
81101
|
-
const response = await fetch(`http://127.0.0.1:${config.PORT}/v1/messages`, {
|
|
81102
|
-
method: "POST",
|
|
81103
|
-
headers: {
|
|
81104
|
-
"x-api-key": config.APIKEY,
|
|
81105
|
-
"content-type": "application/json"
|
|
81106
|
-
},
|
|
81107
|
-
body: JSON.stringify(req.body)
|
|
81108
|
-
});
|
|
81109
|
-
if (!response.ok) {
|
|
81110
|
-
return void 0;
|
|
81111
|
-
}
|
|
81112
|
-
const stream = response.body.pipeThrough(new SSEParserTransform());
|
|
81113
|
-
const reader = stream.getReader();
|
|
81114
|
-
while (true) {
|
|
81115
|
-
const { value, done } = await reader.read();
|
|
81116
|
-
if (done) {
|
|
81117
|
-
break;
|
|
81111
|
+
if (!response.ok) {
|
|
81112
|
+
return void 0;
|
|
81118
81113
|
}
|
|
81119
|
-
|
|
81120
|
-
|
|
81114
|
+
const stream = response.body.pipeThrough(new SSEParserTransform());
|
|
81115
|
+
const reader = stream.getReader();
|
|
81116
|
+
while (true) {
|
|
81117
|
+
try {
|
|
81118
|
+
const { value, done: done2 } = await reader.read();
|
|
81119
|
+
if (done2) {
|
|
81120
|
+
break;
|
|
81121
|
+
}
|
|
81122
|
+
if (["message_start", "message_stop"].includes(value.event)) {
|
|
81123
|
+
continue;
|
|
81124
|
+
}
|
|
81125
|
+
if (!controller.desiredSize) {
|
|
81126
|
+
console.log("Stream backpressure detected");
|
|
81127
|
+
break;
|
|
81128
|
+
}
|
|
81129
|
+
controller.enqueue(value);
|
|
81130
|
+
} catch (readError) {
|
|
81131
|
+
if (readError.name === "AbortError" || readError.code === "ERR_STREAM_PREMATURE_CLOSE") {
|
|
81132
|
+
console.log("Stream reading aborted due to client disconnect");
|
|
81133
|
+
abortController.abort();
|
|
81134
|
+
break;
|
|
81135
|
+
}
|
|
81136
|
+
throw readError;
|
|
81137
|
+
}
|
|
81121
81138
|
}
|
|
81122
|
-
|
|
81139
|
+
return void 0;
|
|
81123
81140
|
}
|
|
81124
|
-
return
|
|
81141
|
+
return data;
|
|
81142
|
+
} catch (error) {
|
|
81143
|
+
console.error("Unexpected error in stream processing:", error);
|
|
81144
|
+
if (error.code === "ERR_STREAM_PREMATURE_CLOSE") {
|
|
81145
|
+
console.log("Stream prematurely closed, aborting operations");
|
|
81146
|
+
abortController.abort();
|
|
81147
|
+
return void 0;
|
|
81148
|
+
}
|
|
81149
|
+
throw error;
|
|
81125
81150
|
}
|
|
81126
|
-
|
|
81127
|
-
}).pipeThrough(new SSESerializerTransform());
|
|
81151
|
+
}).pipeThrough(new SSESerializerTransform()));
|
|
81128
81152
|
}
|
|
81129
81153
|
const [originalStream, clonedStream] = payload.tee();
|
|
81130
81154
|
const read = async (stream) => {
|
|
81131
81155
|
const reader = stream.getReader();
|
|
81132
|
-
|
|
81133
|
-
|
|
81134
|
-
|
|
81135
|
-
|
|
81136
|
-
|
|
81137
|
-
|
|
81156
|
+
try {
|
|
81157
|
+
while (true) {
|
|
81158
|
+
const { done: done2, value } = await reader.read();
|
|
81159
|
+
if (done2) break;
|
|
81160
|
+
const dataStr = new TextDecoder().decode(value);
|
|
81161
|
+
if (!dataStr.startsWith("event: message_delta")) {
|
|
81162
|
+
continue;
|
|
81163
|
+
}
|
|
81164
|
+
const str = dataStr.slice(27);
|
|
81165
|
+
try {
|
|
81166
|
+
const message = JSON.parse(str);
|
|
81167
|
+
sessionUsageCache.put(req.sessionId, message.usage);
|
|
81168
|
+
} catch {
|
|
81169
|
+
}
|
|
81138
81170
|
}
|
|
81139
|
-
|
|
81140
|
-
|
|
81141
|
-
|
|
81142
|
-
|
|
81143
|
-
|
|
81171
|
+
} catch (readError) {
|
|
81172
|
+
if (readError.name === "AbortError" || readError.code === "ERR_STREAM_PREMATURE_CLOSE") {
|
|
81173
|
+
console.log("Background read stream closed prematurely");
|
|
81174
|
+
} else {
|
|
81175
|
+
console.error("Error in background stream reading:", readError);
|
|
81144
81176
|
}
|
|
81177
|
+
} finally {
|
|
81178
|
+
reader.releaseLock();
|
|
81145
81179
|
}
|
|
81146
81180
|
};
|
|
81147
81181
|
read(clonedStream);
|
|
81148
|
-
return originalStream;
|
|
81182
|
+
return done(null, originalStream);
|
|
81149
81183
|
}
|
|
81150
81184
|
sessionUsageCache.put(req.sessionId, payload.usage);
|
|
81151
81185
|
}
|
|
81152
|
-
|
|
81186
|
+
if (typeof payload === "object" && payload.error) {
|
|
81187
|
+
done(payload.error, null);
|
|
81188
|
+
}
|
|
81189
|
+
done(null, payload);
|
|
81153
81190
|
});
|
|
81154
81191
|
server.start();
|
|
81155
81192
|
}
|
|
@@ -81241,7 +81278,6 @@ async function executeCodeCommand(args = []) {
|
|
|
81241
81278
|
const claudePath = config?.CLAUDE_PATH || process.env.CLAUDE_PATH || "claude";
|
|
81242
81279
|
const joinedArgs = args.length > 0 ? args.map((arg) => `"${arg.replace(/\"/g, '\\"')}"`).join(" ") : "";
|
|
81243
81280
|
const stdioConfig = config.NON_INTERACTIVE_MODE ? ["pipe", "inherit", "inherit"] : "inherit";
|
|
81244
|
-
console.log(joinedArgs);
|
|
81245
81281
|
const claudeProcess = (0, import_child_process3.spawn)(
|
|
81246
81282
|
claudePath + (joinedArgs ? ` ${joinedArgs}` : ""),
|
|
81247
81283
|
[],
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@musistudio/claude-code-router",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.46",
|
|
4
4
|
"description": "Use Claude Code without an Anthropics account and route it to another LLM provider",
|
|
5
5
|
"bin": {
|
|
6
6
|
"ccr": "./dist/cli.js"
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"license": "MIT",
|
|
21
21
|
"dependencies": {
|
|
22
22
|
"@fastify/static": "^8.2.0",
|
|
23
|
-
"@musistudio/llms": "^1.0.
|
|
23
|
+
"@musistudio/llms": "^1.0.30",
|
|
24
24
|
"dotenv": "^16.4.7",
|
|
25
25
|
"json5": "^2.2.3",
|
|
26
26
|
"openurl": "^1.1.1",
|