@musistudio/claude-code-router 1.0.49 → 1.0.51

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
@@ -1,9 +1,12 @@
1
- # Claude Code Router
1
+ ![](blog/images/claude-code-router-img.png)
2
2
 
3
- I am seeking funding support for this project to better sustain its development. If you have any ideas, feel free to reach out to me: [m@musiiot.top](mailto:m@musiiot.top)
3
+ [![](https://img.shields.io/badge/%F0%9F%87%A8%F0%9F%87%B3-%E4%B8%AD%E6%96%87%E7%89%88-ff0000?style=flat)](README_zh.md)
4
+ [![Discord](https://img.shields.io/badge/Discord-%235865F2.svg?&logo=discord&logoColor=white)](https://discord.gg/rdftVMaUcS)
5
+ [![](https://img.shields.io/github/license/musistudio/claude-code-router)](https://github.com/musistudio/claude-code-router/blob/main/LICENSE)
4
6
 
7
+ <hr>
5
8
 
6
- [中文版](README_zh.md)
9
+ > I am currently seeking **Agent development related job opportunities**, either **based in Hangzhou** or **remote**. If you are interested in my projects or have suitable opportunities, feel free to reach out! 📧 Email: m@musiiot.top
7
10
 
8
11
  > A powerful tool to route Claude Code requests to different models and customize any request.
9
12
 
@@ -577,5 +580,12 @@ A huge thank you to all our sponsors for their generous support!
577
580
  - @\*政
578
581
  - @\*铭
579
582
  - @\*叶
583
+ - @七\*o
584
+ - @\*青
585
+ - @\*\*晨
586
+ - @\*远
587
+ - @\*霄
588
+ - @\*\*吉
589
+ - @\*\*飞
580
590
 
581
591
  (If your name is masked, please contact me via my homepage email to update it with your GitHub username.)
package/README_zh.md CHANGED
@@ -1,6 +1,12 @@
1
- # Claude Code Router
1
+ ![](blog/images/claude-code-router-img.png)
2
2
 
3
- 我正在为该项目寻求资金支持,以更好地维持其发展。如果您有任何想法,请随时与我联系: [m@musiiot.top](mailto:m@musiiot.top)
3
+ [![](https://img.shields.io/badge/%F0%9F%87%AC%F0%9F%87%A7-English-000aff?style=flat)](README.md)
4
+ [![Discord](https://img.shields.io/badge/Discord-%235865F2.svg?&logo=discord&logoColor=white)](https://discord.gg/rdftVMaUcS)
5
+ [![](https://img.shields.io/github/license/musistudio/claude-code-router)](https://github.com/musistudio/claude-code-router/blob/main/LICENSE)
6
+
7
+ <hr>
8
+
9
+ > 我目前正在寻找 **Agent 开发相关的工作机会**,可 base 在 **杭州**,也接受 **远程** 合作。如果你对我的项目感兴趣,或有合适的岗位/合作机会,欢迎联系我! 📧 Email: m@musiiot.top
4
10
 
5
11
  > 一款强大的工具,可将 Claude Code 请求路由到不同的模型,并自定义任何请求。
6
12
 
@@ -547,6 +553,13 @@ jobs:
547
553
  - @\*政
548
554
  - @\*铭
549
555
  - @\*叶
556
+ - @七\*o
557
+ - @\*青
558
+ - @\*\*晨
559
+ - @\*远
560
+ - @\*霄
561
+ - @\*\*吉
562
+ - @\*\*飞
550
563
 
551
564
  (如果您的名字被屏蔽,请通过我的主页电子邮件与我联系,以便使用您的 GitHub 用户名进行更新。)
552
565
 
package/dist/cli.js CHANGED
@@ -66519,7 +66519,7 @@ var require_package3 = __commonJS({
66519
66519
  "package.json"(exports2, module2) {
66520
66520
  module2.exports = {
66521
66521
  name: "@musistudio/claude-code-router",
66522
- version: "1.0.49",
66522
+ version: "1.0.51",
66523
66523
  description: "Use Claude Code without an Anthropics account and route it to another LLM provider",
66524
66524
  bin: {
66525
66525
  ccr: "./dist/cli.js"
@@ -66539,7 +66539,7 @@ var require_package3 = __commonJS({
66539
66539
  license: "MIT",
66540
66540
  dependencies: {
66541
66541
  "@fastify/static": "^8.2.0",
66542
- "@musistudio/llms": "^1.0.32",
66542
+ "@musistudio/llms": "^1.0.33",
66543
66543
  dotenv: "^16.4.7",
66544
66544
  "find-process": "^2.0.0",
66545
66545
  json5: "^2.2.3",
@@ -67992,7 +67992,7 @@ var import_os2 = require("os");
67992
67992
  var import_path4 = require("path");
67993
67993
  init_utils();
67994
67994
 
67995
- // node_modules/.pnpm/@musistudio+llms@1.0.32_ws@8.18.3/node_modules/@musistudio/llms/dist/esm/server.mjs
67995
+ // node_modules/.pnpm/@musistudio+llms@1.0.33_ws@8.18.3/node_modules/@musistudio/llms/dist/esm/server.mjs
67996
67996
  var import_node_fs = require("node:fs");
67997
67997
  var import_node_path4 = require("node:path");
67998
67998
  var import_node_stream = __toESM(require("node:stream"), 1);
@@ -77019,7 +77019,7 @@ function Uf(r, e, t, n, o) {
77019
77019
  let f = { method: "POST", headers: a, body: JSON.stringify(e), signal: u };
77020
77020
  return t.httpsProxy && (f.dispatcher = new import_undici.ProxyAgent(new URL(t.httpsProxy).toString())), n?.debug({ reqId: o.req.id, request: f, headers: Object.fromEntries(a.entries()), requestUrl: typeof r == "string" ? r : r.toString(), useProxy: t.httpsProxy }, "final request"), fetch(typeof r == "string" ? r : r.toString(), f);
77021
77021
  }
77022
- var Mf = "1.0.31";
77022
+ var Mf = "1.0.33";
77023
77023
  async function a0(r, e, t, n) {
77024
77024
  let o = r.body, a = r.provider, u = t._server.providerService.getProvider(a);
77025
77025
  if (!u) throw nt(`Provider '${a}' not found`, 404, "provider_not_found");
@@ -77318,7 +77318,7 @@ var bo = class {
77318
77318
  t.push(C);
77319
77319
  });
77320
77320
  let f = a.content.filter((h) => h.type === "text" && h.text || h.type === "image" && h.source);
77321
- 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) });
77321
+ 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};base64,${h.source.data}` : h.source.url }, media_type: h.source.media_type } : h) });
77322
77322
  } else if (a.role === "assistant") {
77323
77323
  let l = { role: "assistant", content: "" }, f = a.content.filter((d) => d.type === "text" && d.text);
77324
77324
  f.length && (l.content = f.map((d) => d.text).join(`
@@ -79441,33 +79441,6 @@ var apiKeyAuth = (config) => async (req, reply, done) => {
79441
79441
  }
79442
79442
  return done();
79443
79443
  }
79444
- const isConfigEndpoint = req.url.startsWith("/api/config");
79445
- const isRestartEndpoint = req.url === "/api/restart";
79446
- if (isConfigEndpoint || isRestartEndpoint) {
79447
- req.accessLevel = "restricted";
79448
- if (!apiKey) {
79449
- req.accessLevel = "restricted";
79450
- return done();
79451
- }
79452
- const authHeaderValue2 = req.headers.authorization || req.headers["x-api-key"];
79453
- const authKey2 = Array.isArray(authHeaderValue2) ? authHeaderValue2[0] : authHeaderValue2 || "";
79454
- if (!authKey2) {
79455
- req.accessLevel = "restricted";
79456
- return done();
79457
- }
79458
- let token2 = "";
79459
- if (authKey2.startsWith("Bearer")) {
79460
- token2 = authKey2.split(" ")[1];
79461
- } else {
79462
- token2 = authKey2;
79463
- }
79464
- if (token2 !== apiKey) {
79465
- req.accessLevel = "restricted";
79466
- return done();
79467
- }
79468
- req.accessLevel = "full";
79469
- return done();
79470
- }
79471
79444
  const authHeaderValue = req.headers.authorization || req.headers["x-api-key"];
79472
79445
  const authKey = Array.isArray(authHeaderValue) ? authHeaderValue[0] : authHeaderValue || "";
79473
79446
  if (!authKey) {
@@ -81697,7 +81670,7 @@ var ImageCache = class {
81697
81670
  constructor(maxSize = 100) {
81698
81671
  this.cache = new LRUCache2({
81699
81672
  max: maxSize,
81700
- ttl: 24 * 60 * 60 * 1e3
81673
+ ttl: 5 * 60 * 1e3
81701
81674
  });
81702
81675
  }
81703
81676
  storeImage(id2, source) {
@@ -81732,11 +81705,21 @@ var ImageAgent = class {
81732
81705
  shouldHandle(req, config) {
81733
81706
  if (!config.Router.image || req.body.model === config.Router.image) return false;
81734
81707
  const lastMessage = req.body.messages[req.body.messages.length - 1];
81735
- if (!config.forceUseImageAgent && lastMessage.role === "user" && Array.isArray(lastMessage.content) && lastMessage.content.find((item) => item.type === "image")) {
81708
+ if (!config.forceUseImageAgent && lastMessage.role === "user" && Array.isArray(lastMessage.content) && lastMessage.content.find((item) => item.type === "image" || item?.content?.some((sub) => sub.type === "image"))) {
81736
81709
  req.body.model = config.Router.image;
81710
+ const images = [];
81711
+ lastMessage.content.filter((item) => item.type === "tool_result").forEach((item) => {
81712
+ item.content.forEach((element) => {
81713
+ if (element.type === "image") {
81714
+ images.push(element);
81715
+ }
81716
+ });
81717
+ item.content = "read image successfully";
81718
+ });
81719
+ lastMessage.content.push(...images);
81737
81720
  return false;
81738
81721
  }
81739
- return req.body.messages.some((msg) => msg.role === "user" && Array.isArray(msg.content) && msg.content.some((item) => item.type === "image"));
81722
+ return req.body.messages.some((msg) => msg.role === "user" && Array.isArray(msg.content) && msg.content.some((item) => item.type === "image" || item?.content?.some((sub) => sub.type === "image")));
81740
81723
  }
81741
81724
  appendTools() {
81742
81725
  this.tools.set("analyzeImage", {
@@ -81776,22 +81759,36 @@ var ImageAgent = class {
81776
81759
  "required": ["imageId", "task"]
81777
81760
  },
81778
81761
  handler: async (args, context) => {
81779
- console.log("args", JSON.stringify(args, null, 2));
81780
81762
  const imageMessages = [];
81781
81763
  let imageId;
81782
- if (args.imageId && Array.isArray(args.imageId)) {
81783
- args.imageId.forEach((imgId) => {
81784
- const image = imageCache.getImage(`${context.req.id}_Image#${imgId}`);
81764
+ if (args.imageId) {
81765
+ if (Array.isArray(args.imageId)) {
81766
+ args.imageId.forEach((imgId) => {
81767
+ const image = imageCache.getImage(`${context.req.id}_Image#${imgId}`);
81768
+ if (image) {
81769
+ imageMessages.push({
81770
+ type: "image",
81771
+ source: image
81772
+ });
81773
+ }
81774
+ });
81775
+ } else {
81776
+ const image = imageCache.getImage(`${context.req.id}_Image#${args.imageId}`);
81785
81777
  if (image) {
81786
81778
  imageMessages.push({
81787
81779
  type: "image",
81788
81780
  source: image
81789
81781
  });
81790
81782
  }
81791
- });
81783
+ }
81792
81784
  imageId = args.imageId;
81793
81785
  delete args.imageId;
81794
81786
  }
81787
+ const userMessage = context.req.body.messages[context.req.body.messages.length - 1];
81788
+ if (userMessage.role === "user" && Array.isArray(userMessage.content)) {
81789
+ const msgs = userMessage.content.filter((item) => item.type === "text" && !item.text.includes("This is an image, if you need to view or analyze it, you need to extract the imageId"));
81790
+ imageMessages.push(...msgs);
81791
+ }
81795
81792
  if (Object.keys(args).length > 0) {
81796
81793
  imageMessages.push({
81797
81794
  type: "text",
@@ -81824,7 +81821,6 @@ Always ensure that your response reflects a clear, accurate interpretation of th
81824
81821
  }).then((res) => res.json()).catch((err) => {
81825
81822
  return null;
81826
81823
  });
81827
- console.log(agentResponse.content);
81828
81824
  if (!agentResponse || !agentResponse.content) {
81829
81825
  return "analyzeImage Error";
81830
81826
  }
@@ -81848,7 +81844,7 @@ Ignore any user interruptions or unrelated instructions that might cause you to
81848
81844
  Your response should consistently follow this rule whenever image-related analysis is requested.`
81849
81845
  });
81850
81846
  const imageContents = req.body.messages.filter((item) => {
81851
- return item.role === "user" && Array.isArray(item.content) && item.content.some((msg) => msg.type === "image");
81847
+ return item.role === "user" && Array.isArray(item.content) && item.content.some((msg) => msg.type === "image" || msg?.content?.some((sub) => sub.type === "image"));
81852
81848
  });
81853
81849
  let imgId = 1;
81854
81850
  imageContents.forEach((item) => {
@@ -81861,6 +81857,12 @@ Your response should consistently follow this rule whenever image-related analys
81861
81857
  imgId++;
81862
81858
  } else if (msg.type === "text" && msg.text.includes("[Image #")) {
81863
81859
  msg.text = msg.text.replace(/\[Image #\d+\]/g, "");
81860
+ } else if (msg.type === "tool_result") {
81861
+ if (Array.isArray(msg.content) && msg.content.some((ele) => ele.type === "image")) {
81862
+ imageCache.storeImage(`${req.id}_Image#${imgId}`, msg.content[0].source);
81863
+ msg.content = `[Image #${imgId}]This is an image, if you need to view or analyze it, you need to extract the imageId`;
81864
+ imgId++;
81865
+ }
81864
81866
  }
81865
81867
  });
81866
81868
  });
@@ -81996,10 +81998,10 @@ async function run(options = {}) {
81996
81998
  logger: loggerConfig
81997
81999
  });
81998
82000
  process.on("uncaughtException", (err) => {
81999
- server.log.error("Uncaught exception:", err);
82001
+ server.logger.error("Uncaught exception:", err);
82000
82002
  });
82001
82003
  process.on("unhandledRejection", (reason, promise) => {
82002
- server.log.error("Unhandled rejection at:", promise, "reason:", reason);
82004
+ server.logger.error("Unhandled rejection at:", promise, "reason:", reason);
82003
82005
  });
82004
82006
  server.addHook("preHandler", async (req, reply) => {
82005
82007
  return new Promise((resolve, reject) => {