@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 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
@@ -539,6 +539,7 @@ jobs:
539
539
  - @zcutlip
540
540
  - [@Peng-YM](http://github.com/Peng-YM)
541
541
  - @\*更
542
+ - @\*.
542
543
 
543
544
  (如果您的名字被屏蔽,请通过我的主页电子邮件与我联系,以便使用您的 GitHub 用户名进行更新。)
544
545
 
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.44",
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.29",
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.29_ws@8.18.3/node_modules/@musistudio/llms/dist/esm/server.mjs
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.29";
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: null }, f = a.content.filter((d) => d.type === "text" && d.text);
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: `\u4F60\u9700\u8981\u6309\u7167\u4EFB\u52A1\u53BB\u89E3\u6790\u56FE\u7247`
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", async (req, reply, payload) => {
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
- if (data.event === "content_block_start" && data?.data?.content_block?.name) {
81051
- const agent = req.agents.find((name) => agents_default.getAgent(name)?.tools.get(data.data.content_block.name));
81052
- if (agent) {
81053
- currentAgent = agents_default.getAgent(agent);
81054
- currentToolIndex = data.data.index;
81055
- currentToolName = data.data.content_block.name;
81056
- currentToolId = data.data.content_block.id;
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
- if (currentToolIndex > -1 && data.data.index === currentToolIndex && data.data?.delta?.type === "input_json_delta") {
81061
- currentToolArgs += data.data?.delta?.partial_json;
81062
- return void 0;
81063
- }
81064
- if (currentToolIndex > -1 && data.data.index === currentToolIndex && data.data.type === "content_block_stop") {
81065
- try {
81066
- const args = import_json52.default.parse(currentToolArgs);
81067
- assistantMessages.push({
81068
- type: "tool_use",
81069
- id: currentToolId,
81070
- name: currentToolName,
81071
- input: args
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
- const toolResult = await currentAgent?.tools.get(currentToolName)?.handler(args, {
81074
- req,
81075
- config
81099
+ req.body.messages.push({
81100
+ role: "user",
81101
+ content: toolMessages
81076
81102
  });
81077
- toolMessages.push({
81078
- "tool_use_id": currentToolId,
81079
- "type": "tool_result",
81080
- "content": toolResult
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
- currentAgent = void 0;
81083
- currentToolIndex = -1;
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
- if (["message_start", "message_stop"].includes(value.event)) {
81120
- continue;
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
- controller.enqueue(value);
81139
+ return void 0;
81123
81140
  }
81124
- return void 0;
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
- return data;
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
- while (true) {
81133
- const { done, value } = await reader.read();
81134
- if (done) break;
81135
- const dataStr = new TextDecoder().decode(value);
81136
- if (!dataStr.startsWith("event: message_delta")) {
81137
- continue;
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
- const str = dataStr.slice(27);
81140
- try {
81141
- const message = JSON.parse(str);
81142
- sessionUsageCache.put(req.sessionId, message.usage);
81143
- } catch {
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
- return payload;
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.44",
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.29",
23
+ "@musistudio/llms": "^1.0.30",
24
24
  "dotenv": "^16.4.7",
25
25
  "json5": "^2.2.3",
26
26
  "openurl": "^1.1.1",