The response has been limited to 50k tokens of the smallest files in the repo. You can remove this limitation by removing the max tokens filter.
├── .dockerignore
├── .gitignore
├── .npmignore
├── CLAUDE.md
├── LICENSE
├── README.md
├── blog
    ├── en
    │   ├── maybe-we-can-do-more-with-the-route.md
    │   └── project-motivation-and-how-it-works.md
    ├── images
    │   ├── alipay.jpg
    │   ├── chrome-devtools.png
    │   ├── chrome-inspect.png
    │   ├── search.png
    │   ├── webstorm-formate-file.png
    │   └── wechat.jpg
    └── zh
    │   ├── 或许我们能在Router中做更多事情.md
    │   └── 项目初衷及原理.md
├── config.json
├── docker-compose.yml
├── dockerfile
├── package-lock.json
├── package.json
├── pnpm-lock.yaml
├── screenshoots
    ├── claude-code.png
    ├── contexterror.jpg
    ├── demo.png
    ├── normal.png
    └── router.png
├── src
    ├── cli.ts
    ├── constants.ts
    ├── index.ts
    ├── server.ts
    └── utils
    │   ├── close.ts
    │   ├── codeCommand.ts
    │   ├── index.ts
    │   ├── log.ts
    │   ├── processCheck.ts
    │   ├── router.ts
    │   └── status.ts
└── tsconfig.json


/.dockerignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | npm-debug.log
3 | 


--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .env
3 | log.txt
4 | .idea
5 | dist


--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
 1 | src
 2 | node_modules
 3 | .claude
 4 | CLAUDE.md
 5 | screenshoots
 6 | .DS_Store
 7 | .vscode
 8 | .idea
 9 | .env
10 | .blog
11 | docs
12 | .log
13 | blog
14 | config.json
15 | 


--------------------------------------------------------------------------------
/CLAUDE.md:
--------------------------------------------------------------------------------
 1 | # CLAUDE.md
 2 | 
 3 | This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.You need use English to write text.
 4 | 
 5 | ## Key Development Commands
 6 | - Build: `npm run build`
 7 | - Start: `npm start`
 8 | 
 9 | ## Architecture
10 | - Uses `express` for routing (see `src/server.ts`)
11 | - Bundles with `esbuild` for CLI distribution
12 | - Plugins are loaded from `$HOME/.claude-code-router/plugins`


--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
 1 | MIT License
 2 | 
 3 | Copyright (c) 2025 musistudio
 4 | 
 5 | Permission is hereby granted, free of charge, to any person obtaining a copy
 6 | of this software and associated documentation files (the "Software"), to deal
 7 | in the Software without restriction, including without limitation the rights
 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 | 
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 | 
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 | 


--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
  1 | # Claude Code Router
  2 | 
  3 | > This is a tool for routing Claude Code requests to different models, and you can customize any request.
  4 | 
  5 | ![](screenshoots/claude-code.png)
  6 | 
  7 | ## Usage
  8 | 
  9 | 1. Install Claude Code
 10 | 
 11 | ```shell
 12 | npm install -g @anthropic-ai/claude-code
 13 | ```
 14 | 
 15 | 2. Install Claude Code Router
 16 | 
 17 | ```shell
 18 | npm install -g @musistudio/claude-code-router
 19 | ```
 20 | 
 21 | 3. Start Claude Code by claude-code-router
 22 | 
 23 | ```shell
 24 | ccr code
 25 | ```
 26 | 
 27 | 4. Configure routing
 28 |    Set up your `~/.claude-code-router/config.json` file like this:
 29 | 
 30 | ```json
 31 | {
 32 |   "Providers": [
 33 |     {
 34 |       "name": "openrouter",
 35 |       // IMPORTANT: api_base_url must be a complete (full) URL.
 36 |       "api_base_url": "https://openrouter.ai/api/v1/chat/completions",
 37 |       "api_key": "sk-xxx",
 38 |       "models": [
 39 |         "google/gemini-2.5-pro-preview",
 40 |         "anthropic/claude-sonnet-4",
 41 |         "anthropic/claude-3.5-sonnet",
 42 |         "anthropic/claude-3.7-sonnet:thinking"
 43 |       ],
 44 |       "transformer": {
 45 |         "use": ["openrouter"]
 46 |       }
 47 |     },
 48 |     {
 49 |       "name": "deepseek",
 50 |       // IMPORTANT: api_base_url must be a complete (full) URL.
 51 |       "api_base_url": "https://api.deepseek.com/chat/completions",
 52 |       "api_key": "sk-xxx",
 53 |       "models": ["deepseek-chat", "deepseek-reasoner"],
 54 |       "transformer": {
 55 |         "use": ["deepseek"],
 56 |         "deepseek-chat": {
 57 |           // Enhance tool usage for the deepseek-chat model using the ToolUse transformer.
 58 |           "use": ["tooluse"]
 59 |         }
 60 |       }
 61 |     },
 62 |     {
 63 |       "name": "ollama",
 64 |       // IMPORTANT: api_base_url must be a complete (full) URL.
 65 |       "api_base_url": "http://localhost:11434/v1/chat/completions",
 66 |       "api_key": "ollama",
 67 |       "models": ["qwen2.5-coder:latest"]
 68 |     },
 69 |     {
 70 |       "name": "gemini",
 71 |       // IMPORTANT: api_base_url must be a complete (full) URL.
 72 |       "api_base_url": "https://generativelanguage.googleapis.com/v1beta/models/",
 73 |       "api_key": "sk-xxx",
 74 |       "models": ["gemini-2.5-flash", "gemini-2.5-pro"],
 75 |       "transformer": {
 76 |         "use": ["gemini"]
 77 |       }
 78 |     },
 79 |     {
 80 |       "name": "volcengine",
 81 |       // IMPORTANT: api_base_url must be a complete (full) URL.
 82 |       "api_base_url": "https://ark.cn-beijing.volces.com/api/v3/chat/completions",
 83 |       "api_key": "sk-xxx",
 84 |       "models": ["deepseek-v3-250324", "deepseek-r1-250528"],
 85 |       "transformer": {
 86 |         "use": ["deepseek"]
 87 |       }
 88 |     },
 89 |     {
 90 |       "name": "siliconflow",
 91 |       // IMPORTANT: api_base_url must be a complete (full) URL.
 92 |       "api_base_url": "https://api.siliconflow.cn/v1/chat/completions",
 93 |       "api_key": "sk-xxx",
 94 |       "models": ["moonshotai/Kimi-K2-Instruct"],
 95 |       "transformer": {
 96 |         "use": [
 97 |           [
 98 |             "maxtoken",
 99 |             {
100 |               "max_tokens": 16384 // for siliconflow max_tokens
101 |             }
102 |           ]
103 |         ]
104 |       }
105 |     }
106 |   ],
107 |   "Router": {
108 |     "default": "deepseek,deepseek-chat", // IMPORTANT OPENAI_MODEL has been deprecated
109 |     "background": "ollama,qwen2.5-coder:latest",
110 |     "think": "deepseek,deepseek-reasoner",
111 |     "longContext": "openrouter,google/gemini-2.5-pro-preview"
112 |   }
113 | }
114 | ```
115 | 
116 | - `background`  
117 |   This model will be used to handle some background tasks([background-token-usage](https://docs.anthropic.com/en/docs/claude-code/costs#background-token-usage)). Based on my tests, it doesn’t require high intelligence. I’m using the qwen-coder-2.5:7b model running locally on my MacBook Pro M1 (32GB) via Ollama.
118 |   If your computer can’t run Ollama, you can also use some free models, such as qwen-coder-2.5:3b.
119 | 
120 | - `think`  
121 |   This model will be used when enabling Claude Code to perform reasoning. However, reasoning budget control has not yet been implemented (since the DeepSeek-R1 model does not support it), so there is currently no difference between using UltraThink and Think modes.
122 |   It is worth noting that Plan Mode also use this model to achieve better planning results.  
123 |   Note: The reasoning process via the official DeepSeek API may be very slow, so you may need to wait for an extended period of time.
124 | 
125 | - `longContext`  
126 |   This model will be used when the context length exceeds 32K (this value may be modified in the future). You can route the request to a model that performs well with long contexts (I’ve chosen google/gemini-2.5-pro-preview). This scenario has not been thoroughly tested yet, so if you encounter any issues, please submit an issue.
127 | 
128 | - model command  
129 |   You can also switch models within Claude Code by using the `/model` command. The format is: `provider,model`, like this:  
130 |   `/model openrouter,anthropic/claude-3.5-sonnet`  
131 |   This will use the anthropic/claude-3.5-sonnet model provided by OpenRouter to handle all subsequent tasks.
132 | 
133 | 5. About transformer
134 | `transformer` is used to convert requests and responses for different vendors. For different vendors, we can configure different transformers.
135 | 
136 | For example, in the following case, we use the `openrouter` transformer for the OpenRouter vendor. This transformer removes the `cache_control` parameter (mainly used to adapt Claude's prompt cache) from the request for models other than Claude. In the response, it adapts the reasoning field.
137 | ```json
138 | {
139 |   "name": "openrouter",
140 |   "api_base_url": "https://openrouter.ai/api/v1/chat/completions",
141 |   "api_key": "",
142 |   "models": [
143 |     "google/gemini-2.5-pro-preview",
144 |     "anthropic/claude-sonnet-4",
145 |     "anthropic/claude-3.5-sonnet",
146 |     "anthropic/claude-3.7-sonnet:thinking",
147 |     "deepseek/deepseek-chat-v3-0324"
148 |   ],
149 |   "transformer": {
150 |     "use": [
151 |       "openrouter"
152 |     ]
153 |   }
154 | }
155 | ```
156 | You can also configure transformers for different models of the same vendor. For instance, in the following example, we use the `deepseek` transformer for the DeepSeek vendor. This transformer sets the maximum value of `max_tokens` to `8192` in the request, and in the response, it adapts the `reasoning_content` field. Additionally, for the `deepseek-chat` model, we use the `tooluse` transformer, which optimizes the tool call for the `deepseek-v3` model using the `tool_choice` parameter (mainly because deepseek-r1 does not support the tool_choice parameter).
157 | ```json
158 | {
159 |   "name": "deepseek",
160 |   "api_base_url": "https://api.deepseek.com/chat/completions",
161 |   "api_key": "",
162 |   "models": [
163 |     "deepseek-chat",
164 |     "deepseek-reasoner"
165 |   ],
166 |   "transformer": {
167 |     "use": [
168 |       "deepseek"
169 |     ],
170 |     "deepseek-chat": {
171 |       "use": [
172 |         "tooluse"
173 |       ]
174 |     }
175 |   }
176 | }
177 | ```
178 | Currently, the following transformers are available:
179 | 
180 | - deepseek
181 | 
182 | - gemini
183 | 
184 | - maxtoken
185 | 
186 | - openrouter
187 | 
188 | - tooluse
189 | 
190 | - gemini-cli (experimental, unofficial support: https://gist.github.com/musistudio/1c13a65f35916a7ab690649d3df8d1cd)
191 | 
192 | You can configure custom transformers in the `config.json` file using the `transformers` field, for example:
193 | ```json
194 | {
195 |   "transformers": [
196 |       {
197 |         "path": "$HOME/.claude-code-router/plugins/gemini-cli.js",
198 |         "options": {
199 |           "project": "xxx"
200 |         }
201 |       }
202 |   ]
203 | }
204 | ```
205 | 
206 | ## Features
207 | 
208 | - [x] Support change models
209 | - [x] Github Actions
210 | - [ ] More detailed logs
211 | - [ ] Support image
212 | - [ ] Support web search
213 | 
214 | ## Github Actions
215 | 
216 | You just need to install `Claude Code Actions` in your repository according to the [official documentation](https://docs.anthropic.com/en/docs/claude-code/github-actions). For `ANTHROPIC_API_KEY`, you can use any string. Then, modify your `.github/workflows/claude.yaml` file to include claude-code-router, like this:
217 | 
218 | ```yaml
219 | name: Claude Code
220 | 
221 | on:
222 |   issue_comment:
223 |     types: [created]
224 |   pull_request_review_comment:
225 |     types: [created]
226 |   issues:
227 |     types: [opened, assigned]
228 |   pull_request_review:
229 |     types: [submitted]
230 | 
231 | jobs:
232 |   claude:
233 |     if: |
234 |       (github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude')) ||
235 |       (github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude')) ||
236 |       (github.event_name == 'pull_request_review' && contains(github.event.review.body, '@claude')) ||
237 |       (github.event_name == 'issues' && (contains(github.event.issue.body, '@claude') || contains(github.event.issue.title, '@claude')))
238 |     runs-on: ubuntu-latest
239 |     permissions:
240 |       contents: read
241 |       pull-requests: read
242 |       issues: read
243 |       id-token: write
244 |     steps:
245 |       - name: Checkout repository
246 |         uses: actions/checkout@v4
247 |         with:
248 |           fetch-depth: 1
249 | 
250 |       - name: Prepare Environment
251 |         run: |
252 |           curl -fsSL https://bun.sh/install | bash
253 |           mkdir -p $HOME/.claude-code-router
254 |           cat << 'EOF' > $HOME/.claude-code-router/config.json
255 |           {
256 |             "log": true,
257 |             "OPENAI_API_KEY": "${{ secrets.OPENAI_API_KEY }}",
258 |             "OPENAI_BASE_URL": "https://api.deepseek.com",
259 |             "OPENAI_MODEL": "deepseek-chat"
260 |           }
261 |           EOF
262 |         shell: bash
263 | 
264 |       - name: Start Claude Code Router
265 |         run: |
266 |           nohup ~/.bun/bin/bunx @musistudio/claude-code-router@1.0.8 start &
267 |         shell: bash
268 | 
269 |       - name: Run Claude Code
270 |         id: claude
271 |         uses: anthropics/claude-code-action@beta
272 |         env:
273 |           ANTHROPIC_BASE_URL: http://localhost:3456
274 |         with:
275 |           anthropic_api_key: "test"
276 | ```
277 | 
278 | You can modify the contents of `$HOME/.claude-code-router/config.json` as needed.
279 | GitHub Actions support allows you to trigger Claude Code at specific times, which opens up some interesting possibilities.
280 | 
281 | For example, between 00:30 and 08:30 Beijing Time, using the official DeepSeek API:
282 | 
283 | - The cost of the `deepseek-v3` model is only 50% of the normal time.
284 | 
285 | - The `deepseek-r1` model is just 25% of the normal time.
286 | 
287 | So maybe in the future, I’ll describe detailed tasks for Claude Code ahead of time and let it run during these discounted hours to reduce costs?
288 | 
289 | ## Some tips:
290 | 
291 | Now you can use deepseek-v3 models directly without using any plugins.
292 | 
293 | If you’re using the DeepSeek API provided by the official website, you might encounter an “exceeding context” error after several rounds of conversation (since the official API only supports a 64K context window). In this case, you’ll need to discard the previous context and start fresh. Alternatively, you can use ByteDance’s DeepSeek API, which offers a 128K context window and supports KV cache.
294 | 
295 | ![](screenshoots/contexterror.jpg)
296 | 
297 | Note: claude code consumes a huge amount of tokens, but thanks to DeepSeek’s low cost, you can use claude code at a fraction of Claude’s price, and you don’t need to subscribe to the Claude Max plan.
298 | 
299 | Some interesting points: Based on my testing, including a lot of context information can help narrow the performance gap between these LLM models. For instance, when I used Claude-4 in VSCode Copilot to handle a Flutter issue, it messed up the files in three rounds of conversation, and I had to roll everything back. However, when I used claude code with DeepSeek, after three or four rounds of conversation, I finally managed to complete my task—and the cost was less than 1 RMB!
300 | 
301 | ## Some articles:
302 | 
303 | 1. [Project Motivation and Principles](blog/en/project-motivation-and-how-it-works.md) ([项目初衷及原理](blog/zh/项目初衷及原理.md))
304 | 2. [Maybe We Can Do More with the Router](blog/en/maybe-we-can-do-more-with-the-route.md) ([或许我们能在 Router 中做更多事情](blog/zh/或许我们能在Router中做更多事情.md))
305 | 
306 | ## Buy me a coffee
307 | 
308 | If you find this project helpful, you can choose to sponsor the author with a cup of coffee. Please provide your GitHub information so I can add you to the sponsor list below.
309 | 
310 | [![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/F1F31GN2GM)
311 | 
312 | <table>
313 |   <tr>
314 |     <td><img src="/blog/images/alipay.jpg" width="200" /></td>
315 |     <td><img src="/blog/images/wechat.jpg" width="200" /></td>
316 |   </tr>
317 | </table>
318 | 
319 | ## Sponsors
320 | 
321 | Thanks to the following sponsors for supporting the continued development of this project:
322 | 
323 | @Simon Leischnig (If you see this, feel free to contact me and I can update it with your GitHub information)  
324 | [@duanshuaimin](https://github.com/duanshuaimin)  
325 | [@vrgitadmin](https://github.com/vrgitadmin)  
326 | @\*o (可通过主页邮箱联系我修改 github 用户名)  
327 | [@ceilwoo](https://github.com/ceilwoo)      
328 | @\*说 (可通过主页邮箱联系我修改 github 用户名)  
329 | @\*更 (可通过主页邮箱联系我修改 github 用户名)  
330 | @K\*g (可通过主页邮箱联系我修改 github 用户名)  
331 | @R\*R (可通过主页邮箱联系我修改 github 用户名)  
332 | [@bobleer](https://github.com/bobleer)     
333 | @\*苗 (可通过主页邮箱联系我修改 github 用户名)  
334 | @\*划 (可通过主页邮箱联系我修改 github 用户名)     
335 | [@Clarence-pan](https://github.com/Clarence-pan)     
336 | [@carter003](https://github.com/carter003)      
337 | @S\*r (可通过主页邮箱联系我修改 github 用户名)     
338 | @\*晖 (可通过主页邮箱联系我修改 github 用户名)      
339 | @\*敏 (可通过主页邮箱联系我修改 github 用户名)      
340 | @Z\*z (可通过主页邮箱联系我修改 github 用户名)      
341 | @\*然 (可通过主页邮箱联系我修改 github 用户名)      
342 | [@cluic](https://github.com/cluic)        
343 | @\*苗 (可通过主页邮箱联系我修改 github 用户名)    
344 | [@PromptExpert](https://github.com/PromptExpert)        
345 | @\*应 (可通过主页邮箱联系我修改 github 用户名)    
346 | 


--------------------------------------------------------------------------------
/blog/en/maybe-we-can-do-more-with-the-route.md:
--------------------------------------------------------------------------------
  1 | # Maybe We Can Do More with the Router
  2 | 
  3 | Since the release of `claude-code-router`, I’ve received a lot of user feedback, and quite a few issues are still open. Most of them are related to support for different providers and the lack of tool usage from the deepseek model.
  4 | 
  5 | Originally, I created this project for personal use, mainly to access claude code at a lower cost. So, multi-provider support wasn’t part of the initial design. But during troubleshooting, I discovered that even though most providers claim to be compatible with the OpenAI-style `/chat/completions` interface, there are many subtle differences. For example:
  6 | 
  7 | 1. When Gemini's tool parameter type is string, the `format` field only supports `date` and `date-time`, and there’s no tool call ID.
  8 | 
  9 | 2. OpenRouter requires `cache_control` for caching.
 10 | 
 11 | 3. The official DeepSeek API has a `max_output` of 8192, but Volcano Engine’s limit is even higher.
 12 | 
 13 | Aside from these, smaller providers often have quirks in their parameter handling. So I decided to create a new project, [musistudio/llms](https://github.com/musistudio/llms), to deal with these compatibility issues. It uses the OpenAI format as a base and introduces a generic Transformer interface for transforming both requests and responses.
 14 | 
 15 | Once a `Transformer` is implemented for each provider, it becomes possible to mix-and-match requests between them. For example, I implemented bidirectional conversion between Anthropic and OpenAI formats in `AnthropicTransformer`, which listens to the `/v1/messages` endpoint. Similarly, `GeminiTransformer` handles Gemini <-> OpenAI format conversions and listens to `/v1beta/models/:modelAndAction`.
 16 | 
 17 | When both requests and responses are transformed into a common format, they can interoperate seamlessly:
 18 | 
 19 | ```
 20 | AnthropicRequest -> AnthropicTransformer -> OpenAIRequest -> GeminiTransformer -> GeminiRequest -> GeminiServer
 21 | ```
 22 | 
 23 | ```
 24 | GeminiResponse -> GeminiTransformer -> OpenAIResponse -> AnthropicTransformer -> AnthropicResponse
 25 | ```
 26 | 
 27 | Using a middleware layer to smooth out differences may introduce some performance overhead, but the main goal here is to enable `claude-code-router` to support multiple providers.
 28 | 
 29 | As for the issue of DeepSeek’s lackluster tool usage — I found that it stems from poor instruction adherence in long conversations. Initially, the model actively calls tools, but after several rounds, it starts responding with plain text instead. My first workaround was injecting a system prompt to remind the model to use tools proactively. But in long contexts, the model tends to forget this instruction.
 30 | 
 31 | After reading the DeepSeek documentation, I noticed it supports the `tool_choice` parameter, which can be set to `"required"` to force the model to use at least one tool. I tested this by enabling the parameter, and it significantly improved the model’s tool usage. We can remove the setting when it's no longer necessary. With the help of the `Transformer` interface in [musistudio/llms](https://github.com/musistudio/llms), we can modify the request before it’s sent and adjust the response after it’s received.
 32 | 
 33 | Inspired by the Plan Mode in `claude code`, I implemented a similar Tool Mode for DeepSeek:
 34 | 
 35 | ```typescript
 36 | export class TooluseTransformer implements Transformer {
 37 |   name = "tooluse";
 38 | 
 39 |   transformRequestIn(request: UnifiedChatRequest): UnifiedChatRequest {
 40 |     if (request.tools?.length) {
 41 |       request.messages.push({
 42 |         role: "system",
 43 |         content: `<system-reminder>Tool mode is active. The user expects you to proactively execute the most suitable tool to help complete the task. 
 44 | Before invoking a tool, you must carefully evaluate whether it matches the current task. If no available tool is appropriate for the task, you MUST call the \`ExitTool\` to exit tool mode — this is the only valid way to terminate tool mode.
 45 | Always prioritize completing the user's task effectively and efficiently by using tools whenever appropriate.</system-reminder>`,
 46 |       });
 47 |       request.tool_choice = "required";
 48 |       request.tools.unshift({
 49 |         type: "function",
 50 |         function: {
 51 |           name: "ExitTool",
 52 |           description: `Use this tool when you are in tool mode and have completed the task. This is the only valid way to exit tool mode.
 53 | IMPORTANT: Before using this tool, ensure that none of the available tools are applicable to the current task. You must evaluate all available options — only if no suitable tool can help you complete the task should you use ExitTool to terminate tool mode.
 54 | Examples:
 55 | 1. Task: "Use a tool to summarize this document" — Do not use ExitTool if a summarization tool is available.
 56 | 2. Task: "What’s the weather today?" — If no tool is available to answer, use ExitTool after reasoning that none can fulfill the task.`,
 57 |           parameters: {
 58 |             type: "object",
 59 |             properties: {
 60 |               response: {
 61 |                 type: "string",
 62 |                 description:
 63 |                   "Your response will be forwarded to the user exactly as returned — the tool will not modify or post-process it in any way.",
 64 |               },
 65 |             },
 66 |             required: ["response"],
 67 |           },
 68 |         },
 69 |       });
 70 |     }
 71 |     return request;
 72 |   }
 73 | 
 74 |   async transformResponseOut(response: Response): Promise<Response> {
 75 |     if (response.headers.get("Content-Type")?.includes("application/json")) {
 76 |       const jsonResponse = await response.json();
 77 |       if (
 78 |         jsonResponse?.choices[0]?.message.tool_calls?.length &&
 79 |         jsonResponse?.choices[0]?.message.tool_calls[0]?.function?.name ===
 80 |           "ExitTool"
 81 |       ) {
 82 |         const toolArguments = JSON.parse(toolCall.function.arguments || "{}");
 83 |         jsonResponse.choices[0].message.content = toolArguments.response || "";
 84 |         delete jsonResponse.choices[0].message.tool_calls;
 85 |       }
 86 | 
 87 |       // Handle non-streaming response if needed
 88 |       return new Response(JSON.stringify(jsonResponse), {
 89 |         status: response.status,
 90 |         statusText: response.statusText,
 91 |         headers: response.headers,
 92 |       });
 93 |     } else if (response.headers.get("Content-Type")?.includes("stream")) {
 94 |       // ...
 95 |     }
 96 |     return response;
 97 |   }
 98 | }
 99 | ```
100 | 
101 | This transformer ensures the model calls at least one tool. If no tools are appropriate or the task is finished, it can exit using `ExitTool`. Since this relies on the `tool_choice` parameter, it only works with models that support it.
102 | 
103 | In practice, this approach noticeably improves tool usage for DeepSeek. The tradeoff is that sometimes the model may invoke irrelevant or unnecessary tools, which could increase latency and token usage.
104 | 
105 | This update is just a small experiment — adding an `“agent”` to the router. Maybe there are more interesting things we can explore from here.


--------------------------------------------------------------------------------
/blog/en/project-motivation-and-how-it-works.md:
--------------------------------------------------------------------------------
  1 | # Project Motivation and Principles
  2 | 
  3 | As early as the day after Claude Code was released (2025-02-25), I began and completed a reverse engineering attempt of the project. At that time, using Claude Code required registering for an Anthropic account, applying for a waitlist, and waiting for approval. However, due to well-known reasons, Anthropic blocks users from mainland China, making it impossible for me to use the service through normal means. Based on known information, I discovered the following:
  4 | 
  5 | 1. Claude Code is installed via npm, so it's very likely developed with Node.js.
  6 | 2. Node.js offers various debugging methods: simple `console.log` usage, launching with `--inspect` to hook into Chrome DevTools, or even debugging obfuscated code using `d8`.
  7 | 
  8 | My goal was to use Claude Code without an Anthropic account. I didn’t need the full source code—just a way to intercept and reroute requests made by Claude Code to Anthropic’s models to my own custom endpoint. So I started the reverse engineering process:
  9 | 
 10 | 1. First, install Claude Code:
 11 | ```bash
 12 | npm install -g @anthropic-ai/claude-code
 13 | ```
 14 | 
 15 | 2. After installation, the project is located at `~/.nvm/versions/node/v20.10.0/lib/node_modules/@anthropic-ai/claude-code`(this may vary depending on your Node version manager and version).
 16 | 
 17 | 3. Open the package.json to analyze the entry point:
 18 | ```package.json
 19 | {
 20 |   "name": "@anthropic-ai/claude-code",
 21 |   "version": "1.0.24",
 22 |   "main": "sdk.mjs",
 23 |   "types": "sdk.d.ts",
 24 |   "bin": {
 25 |     "claude": "cli.js"
 26 |   },
 27 |   "engines": {
 28 |     "node": ">=18.0.0"
 29 |   },
 30 |   "type": "module",
 31 |   "author": "Boris Cherny <boris@anthropic.com>",
 32 |   "license": "SEE LICENSE IN README.md",
 33 |   "description": "Use Claude, Anthropic's AI assistant, right from your terminal. Claude can understand your codebase, edit files, run terminal commands, and handle entire workflows for you.",
 34 |   "homepage": "https://github.com/anthropics/claude-code",
 35 |   "bugs": {
 36 |     "url": "https://github.com/anthropics/claude-code/issues"
 37 |   },
 38 |   "scripts": {
 39 |     "prepare": "node -e \"if (!process.env.AUTHORIZED) { console.error('ERROR: Direct publishing is not allowed.\\nPlease use the publish-external.sh script to publish this package.'); process.exit(1); }\"",
 40 |     "preinstall": "node scripts/preinstall.js"
 41 |   },
 42 |   "dependencies": {},
 43 |   "optionalDependencies": {
 44 |     "@img/sharp-darwin-arm64": "^0.33.5",
 45 |     "@img/sharp-darwin-x64": "^0.33.5",
 46 |     "@img/sharp-linux-arm": "^0.33.5",
 47 |     "@img/sharp-linux-arm64": "^0.33.5",
 48 |     "@img/sharp-linux-x64": "^0.33.5",
 49 |     "@img/sharp-win32-x64": "^0.33.5"
 50 |   }
 51 | }
 52 | ```
 53 | 
 54 | The key entry is `"claude": "cli.js"`. Opening cli.js, you'll see the code is minified and obfuscated. But using WebStorm’s `Format File` feature, you can reformat it for better readability:
 55 | ![webstorm-formate-file](../images/webstorm-formate-file.png)
 56 | 
 57 | Now you can begin understanding Claude Code’s internal logic and prompt structure by reading the code. To dig deeper, you can insert console.log statements or launch in debug mode with Chrome DevTools using:
 58 | 
 59 | ```bash
 60 | NODE_OPTIONS="--inspect-brk=9229" claude
 61 | ```
 62 | 
 63 | This command starts Claude Code in debug mode and opens port 9229. Visit chrome://inspect/ in Chrome and click inspect to begin debugging:
 64 | ![chrome-devtools](../images/chrome-inspect.png)
 65 | ![chrome-devtools](../images/chrome-devtools.png)
 66 | 
 67 | By searching for the keyword api.anthropic.com, you can easily locate where Claude Code makes its API calls. From the surrounding code, it's clear that baseURL can be overridden with the `ANTHROPIC_BASE_URL` environment variable, and `apiKey` and `authToken` can be configured similarly:
 68 | ![search](../images/search.png)
 69 | 
 70 | So far, we’ve discovered some key information:
 71 | 
 72 | 1. Environment variables can override Claude Code's `baseURL` and `apiKey`.
 73 | 
 74 | 2. Claude Code adheres to the Anthropic API specification.
 75 | 
 76 | Therefore, we need:
 77 | 1. A service to convert OpenAI API–compatible requests into Anthropic API format.
 78 | 
 79 | 2. Set the environment variables before launching Claude Code to redirect requests to this service.
 80 | 
 81 | Thus, `claude-code-router` was born. This project uses `Express.js` to implement the `/v1/messages` endpoint. It leverages middlewares to transform request/response formats and supports request rewriting (useful for prompt tuning per model).
 82 | 
 83 | Back in February, the full DeepSeek model series had poor support for Function Calling, so I initially used `qwen-max`. It worked well—but without KV cache support, it consumed a large number of tokens and couldn’t provide the native `Claude Code` experience.
 84 | 
 85 | So I experimented with a Router-based mode using a lightweight model to dispatch tasks. The architecture included four roles: `router`, `tool`, `think`, and `coder`. Each request passed through a free lightweight model that would decide whether the task involved reasoning, coding, or tool usage. Reasoning and coding tasks looped until a tool was invoked to apply changes. However, the lightweight model lacked the capability to route tasks accurately, and architectural issues prevented it from effectively driving Claude Code.
 86 | 
 87 | Everything changed at the end of May when the official Claude Code was launched, and `DeepSeek-R1` model (released 2025-05-28) added Function Call support. I redesigned the system. With the help of AI pair programming, I fixed earlier request/response transformation issues—especially the handling of models that return JSON instead of Function Call outputs.
 88 | 
 89 | This time, I used the `DeepSeek-V3`  model. It performed better than expected: supporting most tool calls, handling task decomposition and stepwise planning, and—most importantly—costing less than one-tenth the price of Claude 3.5 Sonnet.
 90 | 
 91 | The official Claude Code organizes agents differently from the beta version, so I restructured my Router mode to include four roles: the default model, `background`, `think`, and `longContext`.
 92 | 
 93 | - The default model handles general tasks and acts as a fallback.
 94 | 
 95 | - The `background` model manages lightweight background tasks. According to Anthropic, Claude Haiku 3.5 is often used here, so I routed this to a local `ollama` service.
 96 | 
 97 | - The `think` model is responsible for reasoning and planning mode tasks. I use `DeepSeek-R1` here, though it doesn’t support cost control, so `Think` and `UltraThink` behave identically.
 98 | 
 99 | - The `longContext` model handles long-context scenarios. The router uses `tiktoken` to calculate token lengths in real time, and if the context exceeds 32K, it switches to this model to compensate for DeepSeek's long-context limitations.
100 | 
101 | This describes the evolution and reasoning behind the project. By cleverly overriding environment variables, we can forward and modify requests without altering Claude Code’s source—allowing us to benefit from official updates while using our own models and custom prompts.
102 | 
103 | This project offers a practical approach to running Claude Code under Anthropic’s regional restrictions, balancing `cost`, `performance`, and `customizability`. That said, the official `Max Plan` still offers the best experience if available.


--------------------------------------------------------------------------------
/blog/images/alipay.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/musistudio/claude-code-router/5174ddacfcb0cc22cdb274d1266fd978d56537bc/blog/images/alipay.jpg


--------------------------------------------------------------------------------
/blog/images/chrome-devtools.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/musistudio/claude-code-router/5174ddacfcb0cc22cdb274d1266fd978d56537bc/blog/images/chrome-devtools.png


--------------------------------------------------------------------------------
/blog/images/chrome-inspect.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/musistudio/claude-code-router/5174ddacfcb0cc22cdb274d1266fd978d56537bc/blog/images/chrome-inspect.png


--------------------------------------------------------------------------------
/blog/images/search.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/musistudio/claude-code-router/5174ddacfcb0cc22cdb274d1266fd978d56537bc/blog/images/search.png


--------------------------------------------------------------------------------
/blog/images/webstorm-formate-file.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/musistudio/claude-code-router/5174ddacfcb0cc22cdb274d1266fd978d56537bc/blog/images/webstorm-formate-file.png


--------------------------------------------------------------------------------
/blog/images/wechat.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/musistudio/claude-code-router/5174ddacfcb0cc22cdb274d1266fd978d56537bc/blog/images/wechat.jpg


--------------------------------------------------------------------------------
/blog/zh/或许我们能在Router中做更多事情.md:
--------------------------------------------------------------------------------
 1 | # 或许我们能在 Router 中做更多事情
 2 | 
 3 | 自从`claude-code-router`发布以来,我收到了很多用户的反馈,至今还有不少的 issues 未处理。其中大多都是关于不同的供应商的支持和`deepseek`模型调用工具不积极的问题。
 4 | 之前开发这个项目主要是为了我自己能以较低成本使用上`claude code`,所以一开始的设计并没有考虑到多供应商的情况。在实际的排查问题中,我发现尽管市面上所有的供应商几乎都宣称兼容`OpenAI`格式调用,即通过`/chat/compeletions`接口调用,但是其中的细节差异非常多。例如:
 5 | 
 6 | 1. Gemini 的工具参数类型是 string 时,`format`参数只支持`date`和`date-time`,并且没有工具调用 ID。
 7 | 
 8 | 2. OpenRouter 需要使用`cache_control`进行缓存。
 9 | 
10 | 3. DeepSeek 官方 API 的 `max_output` 为 8192,而火山引擎的会更大。
11 | 
12 | 除了这些问题之外,还有一些其他的小的供应商,他们或多或少参数都有点问题。于是,我打算开发一个新的项目[musistudio/llms](https://github.com/musistudio/llms)来处理这种不同服务商的兼容问题。该项目使用 OpenAI 格式为基础的通用格式,提供了一个`Transformer`接口,该接口用于处理转换请求和响应。当我们给不同的服务商都实现了`Transformer`后,我们可以实现不同服务商的混合调用。比如我在`AnthropicTransformer`中实现了`Anthropic`<->`OpenAI`格式的互相转换,并监听了`/v1/messages`端点,在`GeminiTransformer`中实现了`Gemini`<->`OpenAI`格式的互相转换,并监听了`/v1beta/models/:modelAndAction`端点,当他们的请求和响应都被转换成一个通用格式的时候,就可以实现他们的互相调用。
13 | 
14 | ```
15 | AnthropicRequest -> AnthropicTransformer -> OpenAIRequest -> GeminiTransformer -> GeminiRequest -> GeminiServer
16 | ```
17 | 
18 | ```
19 | GeminiReseponse -> GeminiTransformer -> OpenAIResponse -> AnthropicTransformer -> AnthropicResponse
20 | ```
21 | 
22 | 虽然使用中间层抹平差异可能会带来一些性能问题,但是该项目最初的目的是为了让`claude-code-router`支持不同的供应商。
23 | 
24 | 至于`deepseek`模型调用工具不积极的问题,我发现这是由于`deepseek`在长上下文中的指令遵循不佳导致的。现象就是刚开始模型会主动调用工具,但是在经过几轮对话后模型只会返回文本。一开始的解决方案是通过注入一个系统提示词告知模型需要积极去使用工具以解决用户的问题,但是后面测试发现在长上下文中模型会遗忘该指令。
25 | 查看`deepseek`文档后发现模型支持`tool_choice`参数,可以强制让模型最少调用 1 个工具,我尝试将该值设置为`required`,发现模型调用工具的积极性大大增加,现在我们只需要在合适的时候取消这个参数即可。借助[musistudio/llms](https://github.com/musistudio/llms)的`Transformer`可以让我们在发送请求前和收到响应后做点什么,所以我参考`claude code`的`Plan Mode`,实现了一个使用与`deepseek`的`Tool Mode`
26 | 
27 | ```typescript
28 | export class TooluseTransformer implements Transformer {
29 |   name = "tooluse";
30 | 
31 |   transformRequestIn(request: UnifiedChatRequest): UnifiedChatRequest {
32 |     if (request.tools?.length) {
33 |       request.messages.push({
34 |         role: "system",
35 |         content: `<system-reminder>Tool mode is active. The user expects you to proactively execute the most suitable tool to help complete the task. 
36 | Before invoking a tool, you must carefully evaluate whether it matches the current task. If no available tool is appropriate for the task, you MUST call the \`ExitTool\` to exit tool mode — this is the only valid way to terminate tool mode.
37 | Always prioritize completing the user's task effectively and efficiently by using tools whenever appropriate.</system-reminder>`,
38 |       });
39 |       request.tool_choice = "required";
40 |       request.tools.unshift({
41 |         type: "function",
42 |         function: {
43 |           name: "ExitTool",
44 |           description: `Use this tool when you are in tool mode and have completed the task. This is the only valid way to exit tool mode.
45 | IMPORTANT: Before using this tool, ensure that none of the available tools are applicable to the current task. You must evaluate all available options — only if no suitable tool can help you complete the task should you use ExitTool to terminate tool mode.
46 | Examples:
47 | 1. Task: "Use a tool to summarize this document" — Do not use ExitTool if a summarization tool is available.
48 | 2. Task: "What’s the weather today?" — If no tool is available to answer, use ExitTool after reasoning that none can fulfill the task.`,
49 |           parameters: {
50 |             type: "object",
51 |             properties: {
52 |               response: {
53 |                 type: "string",
54 |                 description:
55 |                   "Your response will be forwarded to the user exactly as returned — the tool will not modify or post-process it in any way.",
56 |               },
57 |             },
58 |             required: ["response"],
59 |           },
60 |         },
61 |       });
62 |     }
63 |     return request;
64 |   }
65 | 
66 |   async transformResponseOut(response: Response): Promise<Response> {
67 |     if (response.headers.get("Content-Type")?.includes("application/json")) {
68 |       const jsonResponse = await response.json();
69 |       if (
70 |         jsonResponse?.choices[0]?.message.tool_calls?.length &&
71 |         jsonResponse?.choices[0]?.message.tool_calls[0]?.function?.name ===
72 |           "ExitTool"
73 |       ) {
74 |         const toolArguments = JSON.parse(toolCall.function.arguments || "{}");
75 |         jsonResponse.choices[0].message.content = toolArguments.response || "";
76 |         delete jsonResponse.choices[0].message.tool_calls;
77 |       }
78 | 
79 |       // Handle non-streaming response if needed
80 |       return new Response(JSON.stringify(jsonResponse), {
81 |         status: response.status,
82 |         statusText: response.statusText,
83 |         headers: response.headers,
84 |       });
85 |     } else if (response.headers.get("Content-Type")?.includes("stream")) {
86 |       // ...
87 |     }
88 |     return response;
89 |   }
90 | }
91 | ```
92 | 
93 | 该工具将始终让模型至少调用一个工具,如果没有合适的工具或者任务已完成可以调用`ExitTool`来退出工具模式,因为是依靠`tool_choice`参数实现的,所以仅适用于支持该参数的模型。经过测试,该工具能显著增加`deepseek`的工具调用次数,弊端是可能会有跟任务无关或者没有必要的工具调用导致增加任务执行事件和消耗的 `token` 数。
94 | 
95 | 这次更新仅仅是在 Router 中实现一个`agent`的一次小探索,或许还能做更多其他有趣的事也说不定...
96 | 


--------------------------------------------------------------------------------
/blog/zh/项目初衷及原理.md:
--------------------------------------------------------------------------------
 1 | # 项目初衷及原理
 2 | 
 3 | 早在 Claude Code 发布的第二天(2025-02-25),我就尝试并完成了对该项目的逆向。当时要使用 Claude Code 你需要注册一个 Anthropic 账号,然后申请 waitlist,等待通过后才能使用。但是因为众所周知的原因,Anthropic 屏蔽了中国区的用户,所以通过正常手段我无法使用,通过已知的信息,我发现:
 4 | 
 5 | 1. Claude Code 使用 npm 进行安装,所以很大可能其使用 Node.js 进行开发。
 6 | 2. Node.js 调试手段众多,可以简单使用`console.log`获取想要的信息,也可以使用`--inspect`将其接入`Chrome Devtools`,甚至你可以使用`d8`去调试某些加密混淆的代码。
 7 | 
 8 | 由于我的目标是让我在没有 Anthropic 账号的情况下使用`Claude Code`,我并不需要获得完整的源代码,只需要将`Claude Code`请求 Anthropic 模型时将其转发到我自定义的接口即可。接下来我就开启了我的逆向过程:
 9 | 
10 | 1. 首先安装`Claude Code`
11 | 
12 | ```bash
13 | npm install -g @anthropic-ai/claude-code
14 | ```
15 | 
16 | 2. 安装后该项目被放在了`~/.nvm/versions/node/v20.10.0/lib/node_modules/@anthropic-ai/claude-code`中,因为我使用了`nvm`作为我的 node 版本控制器,当前使用`node-v20.10.0`,所以该路径会因人而异。
17 | 3. 找到项目路径之后可通过 package.json 分析包入口,内容如下:
18 | 
19 | ```package.json
20 | {
21 |   "name": "@anthropic-ai/claude-code",
22 |   "version": "1.0.24",
23 |   "main": "sdk.mjs",
24 |   "types": "sdk.d.ts",
25 |   "bin": {
26 |     "claude": "cli.js"
27 |   },
28 |   "engines": {
29 |     "node": ">=18.0.0"
30 |   },
31 |   "type": "module",
32 |   "author": "Boris Cherny <boris@anthropic.com>",
33 |   "license": "SEE LICENSE IN README.md",
34 |   "description": "Use Claude, Anthropic's AI assistant, right from your terminal. Claude can understand your codebase, edit files, run terminal commands, and handle entire workflows for you.",
35 |   "homepage": "https://github.com/anthropics/claude-code",
36 |   "bugs": {
37 |     "url": "https://github.com/anthropics/claude-code/issues"
38 |   },
39 |   "scripts": {
40 |     "prepare": "node -e \"if (!process.env.AUTHORIZED) { console.error('ERROR: Direct publishing is not allowed.\\nPlease use the publish-external.sh script to publish this package.'); process.exit(1); }\"",
41 |     "preinstall": "node scripts/preinstall.js"
42 |   },
43 |   "dependencies": {},
44 |   "optionalDependencies": {
45 |     "@img/sharp-darwin-arm64": "^0.33.5",
46 |     "@img/sharp-darwin-x64": "^0.33.5",
47 |     "@img/sharp-linux-arm": "^0.33.5",
48 |     "@img/sharp-linux-arm64": "^0.33.5",
49 |     "@img/sharp-linux-x64": "^0.33.5",
50 |     "@img/sharp-win32-x64": "^0.33.5"
51 |   }
52 | }
53 | ```
54 | 
55 | 其中`"claude": "cli.js"`就是我们要找的入口,打开 cli.js,发现代码被压缩混淆过了。没关系,借助`webstorm`的`Formate File`功能可以重新格式化,让代码变得稍微好看一点。就像这样:
56 | ![webstorm-formate-file](../images/webstorm-formate-file.png)
57 | 
58 | 现在,你可以通过阅读部分代码来了解`Claude Code`的内容工具原理与提示词。你也可以在关键地方使用`console.log`来获得更多信息,当然,也可以使用`Chrome Devtools`来进行断点调试,使用以下命令启动`Claude Code`:
59 | 
60 | ```bash
61 | NODE_OPTIONS="--inspect-brk=9229" claude
62 | ```
63 | 
64 | 该命令会以调试模式启动`Claude Code`,并将调试的端口设置为`9229`。这时候通过 Chrome 访问`chrome://inspect/`即可看到当前的`Claude Code`进程,点击`inspect`即可进行调试。
65 | ![chrome-devtools](../images/chrome-inspect.png)
66 | ![chrome-devtools](../images/chrome-devtools.png)
67 | 
68 | 通过搜索关键字符`api.anthropic.com`很容易能找到`Claude Code`用来发请求的地方,根据上下文的查看,很容易发现这里的`baseURL`可以通过环境变量`ANTHROPIC_BASE_URL`进行覆盖,`apiKey`和`authToken`也同理。
69 | ![search](../images/search.png)
70 | 
71 | 到目前为止,我们获得关键信息:
72 | 
73 | 1. 可以使用环境变量覆盖`Claude Code`的`BaseURL`和`apiKey`的配置
74 | 
75 | 2. `Claude Code`使用[Anthropic API](https://docs.anthropic.com/en/api/overview)的规范
76 | 
77 | 所以我们需要:
78 | 
79 | 1. 实现一个服务用来将`OpenAI API`的规范转换成`Anthropic API`格式。
80 | 
81 | 2. 启动`Claude Code`之前写入环境变量将`baseURL`指向到该服务。
82 | 
83 | 于是,`claude-code-router`就诞生了,该项目使用`Express.js`作为 HTTP 服务,实现`/v1/messages`端点,使用`middlewares`处理请求/响应的格式转换以及请求重写功能(可以用来重写 Claude Code 的提示词以针对单个模型进行调优)。
84 | 在 2 月份由于`DeepSeek`全系列模型对`Function Call`的支持不佳导致无法直接使用`DeepSeek`模型,所以在当时我选择了`qwen-max`模型,一切表现的都很好,但是`qwen-max`不支持`KV Cache`,意味着我要消耗大量的 token,但是却无法获取`Claude Code`原生的体验。
85 | 所以我又尝试了`Router`模式,即使用一个小模型对任务进行分发,一共分为四个模型:`router`、`tool`、`think`和`coder`,所有的请求先经过一个免费的小模型,由小模型去判断应该是进行思考还是编码还是调用工具,再进行任务的分发,如果是思考和编码任务将会进行循环调用,直到最终使用工具写入或修改文件。但是实践下来发现免费的小模型不足以很好的完成任务的分发,再加上整个 Agnet 的设计存在缺陷,导致并不能很好的驱动`Claude Code`。
86 | 直到 5 月底,`Claude Code`被正式推出,这时`DeepSeek`全系列模型(R1 于 05-28)均支持`Function Call`,我开始重新设计该项目。在与 AI 的结对编程中我修复了之前的请求和响应转换问题,在某些场景下模型输出 JSON 响应而不是`Function Call`。这次直接使用`DeepSeek-v3`模型,它工作的比我想象中要好:能完成绝大多数工具调用,还支持用步骤规划解决任务,最关键的是`DeepSeek`的价格不到`claude Sonnet 3.5`的十分之一。正式发布的`Claude Code`对 Agent 的组织也不同于测试版,于是在分析了`Claude Code`的请求调用之后,我重新组织了`Router`模式:现在它还是四个模型:默认模型、`background`、`think`和`longContext`。
87 | 
88 | - 默认模型作为最终的兜底和日常处理
89 | 
90 | - `background`是用来处理一些后台任务,据 Anthropic 官方说主要用`Claude Haiku 3.5`模型去处理一些小任务,如俳句生成和对话摘要,于是我将其路由到了本地的`ollama`服务。
91 | 
92 | - `think`模型用于让`Claude Code`进行思考或者在`Plan Mode`下使用,这里我使用的是`DeepSeek-R1`,由于其不支持推理成本控制,所以`Think`和`UltraThink`是一样的逻辑。
93 | 
94 | - `longContext`是用于处理长下上文的场景,该项目会对每次请求使用tiktoken实时计算上下文长度,如果上下文大于32K则使用该模型,旨在弥补`DeepSeek`在长上下文处理不佳的情况。
95 | 
96 | 以上就是该项目的发展历程以及我的一些思考,通过巧妙的使用环境变量覆盖的手段在不修改`Claude Code`源码的情况下完成请求的转发和修改,这就使得在可以得到 Anthropic 更新的同时使用自己的模型,自定义自己的提示词。该项目只是在 Anthropic 封禁中国区用户的情况下使用`Claude Code`并且达到成本和性能平衡的一种手段。如果可以的话,还是官方的Max Plan体验最好。
97 | 


--------------------------------------------------------------------------------
/config.json:
--------------------------------------------------------------------------------
1 | {
2 |   "usePlugin": "",
3 |   "LOG": true,
4 |   "OPENAI_API_KEY": "",
5 |   "OPENAI_BASE_URL": "",
6 |   "OPENAI_MODEL": "",
7 |   "modelProviders": {}
8 | }
9 | 


--------------------------------------------------------------------------------
/docker-compose.yml:
--------------------------------------------------------------------------------
 1 | version: "3.8"
 2 | 
 3 | services:
 4 |   claude-code-reverse:
 5 |     build: .
 6 |     ports:
 7 |       - "3456:3456"
 8 |     environment:
 9 |       - ENABLE_ROUTER=${ENABLE_ROUTER}
10 |       - OPENAI_API_KEY=${OPENAI_API_KEY}
11 |       - OPENAI_BASE_URL=${OPENAI_BASE_URL}
12 |       - OPENAI_MODEL=${OPENAI_MODEL}
13 |     restart: unless-stopped
14 | 


--------------------------------------------------------------------------------
/dockerfile:
--------------------------------------------------------------------------------
 1 | FROM node:20-alpine
 2 | 
 3 | WORKDIR /app
 4 | 
 5 | COPY package*.json ./
 6 | RUN npm i
 7 | 
 8 | COPY . .
 9 | 
10 | EXPOSE 3456
11 | 
12 | CMD ["node", "index.mjs"]
13 | 


--------------------------------------------------------------------------------
/package-lock.json:
--------------------------------------------------------------------------------
   1 | {
   2 |   "name": "@musistudio/claude-code-router",
   3 |   "version": "1.0.15",
   4 |   "lockfileVersion": 3,
   5 |   "requires": true,
   6 |   "packages": {
   7 |     "": {
   8 |       "name": "@musistudio/claude-code-router",
   9 |       "version": "1.0.15",
  10 |       "license": "MIT",
  11 |       "dependencies": {
  12 |         "@musistudio/llms": "^1.0.4",
  13 |         "dotenv": "^16.4.7",
  14 |         "tiktoken": "^1.0.21",
  15 |         "uuid": "^11.1.0"
  16 |       },
  17 |       "bin": {
  18 |         "ccr": "dist/cli.js"
  19 |       },
  20 |       "devDependencies": {
  21 |         "esbuild": "^0.25.1",
  22 |         "shx": "^0.4.0",
  23 |         "typescript": "^5.8.2"
  24 |       }
  25 |     },
  26 |     "node_modules/@anthropic-ai/sdk": {
  27 |       "version": "0.54.0",
  28 |       "resolved": "https://registry.npmjs.org/@anthropic-ai/sdk/-/sdk-0.54.0.tgz",
  29 |       "integrity": "sha512-xyoCtHJnt/qg5GG6IgK+UJEndz8h8ljzt/caKXmq3LfBF81nC/BW6E4x2rOWCZcvsLyVW+e8U5mtIr6UCE/kJw==",
  30 |       "license": "MIT",
  31 |       "bin": {
  32 |         "anthropic-ai-sdk": "bin/cli"
  33 |       }
  34 |     },
  35 |     "node_modules/@esbuild/darwin-arm64": {
  36 |       "version": "0.25.5",
  37 |       "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.5.tgz",
  38 |       "integrity": "sha512-GtaBgammVvdF7aPIgH2jxMDdivezgFu6iKpmT+48+F8Hhg5J/sfnDieg0aeG/jfSvkYQU2/pceFPDKlqZzwnfQ==",
  39 |       "cpu": [
  40 |         "arm64"
  41 |       ],
  42 |       "dev": true,
  43 |       "license": "MIT",
  44 |       "optional": true,
  45 |       "os": [
  46 |         "darwin"
  47 |       ],
  48 |       "engines": {
  49 |         "node": ">=18"
  50 |       }
  51 |     },
  52 |     "node_modules/@fastify/ajv-compiler": {
  53 |       "version": "4.0.2",
  54 |       "resolved": "https://registry.npmjs.org/@fastify/ajv-compiler/-/ajv-compiler-4.0.2.tgz",
  55 |       "integrity": "sha512-Rkiu/8wIjpsf46Rr+Fitd3HRP+VsxUFDDeag0hs9L0ksfnwx2g7SPQQTFL0E8Qv+rfXzQOxBJnjUB9ITUDjfWQ==",
  56 |       "funding": [
  57 |         {
  58 |           "type": "github",
  59 |           "url": "https://github.com/sponsors/fastify"
  60 |         },
  61 |         {
  62 |           "type": "opencollective",
  63 |           "url": "https://opencollective.com/fastify"
  64 |         }
  65 |       ],
  66 |       "license": "MIT",
  67 |       "dependencies": {
  68 |         "ajv": "^8.12.0",
  69 |         "ajv-formats": "^3.0.1",
  70 |         "fast-uri": "^3.0.0"
  71 |       }
  72 |     },
  73 |     "node_modules/@fastify/cors": {
  74 |       "version": "11.0.1",
  75 |       "resolved": "https://registry.npmjs.org/@fastify/cors/-/cors-11.0.1.tgz",
  76 |       "integrity": "sha512-dmZaE7M1f4SM8ZZuk5RhSsDJ+ezTgI7v3HHRj8Ow9CneczsPLZV6+2j2uwdaSLn8zhTv6QV0F4ZRcqdalGx1pQ==",
  77 |       "funding": [
  78 |         {
  79 |           "type": "github",
  80 |           "url": "https://github.com/sponsors/fastify"
  81 |         },
  82 |         {
  83 |           "type": "opencollective",
  84 |           "url": "https://opencollective.com/fastify"
  85 |         }
  86 |       ],
  87 |       "license": "MIT",
  88 |       "dependencies": {
  89 |         "fastify-plugin": "^5.0.0",
  90 |         "toad-cache": "^3.7.0"
  91 |       }
  92 |     },
  93 |     "node_modules/@fastify/error": {
  94 |       "version": "4.2.0",
  95 |       "resolved": "https://registry.npmjs.org/@fastify/error/-/error-4.2.0.tgz",
  96 |       "integrity": "sha512-RSo3sVDXfHskiBZKBPRgnQTtIqpi/7zhJOEmAxCiBcM7d0uwdGdxLlsCaLzGs8v8NnxIRlfG0N51p5yFaOentQ==",
  97 |       "funding": [
  98 |         {
  99 |           "type": "github",
 100 |           "url": "https://github.com/sponsors/fastify"
 101 |         },
 102 |         {
 103 |           "type": "opencollective",
 104 |           "url": "https://opencollective.com/fastify"
 105 |         }
 106 |       ],
 107 |       "license": "MIT"
 108 |     },
 109 |     "node_modules/@fastify/fast-json-stringify-compiler": {
 110 |       "version": "5.0.3",
 111 |       "resolved": "https://registry.npmjs.org/@fastify/fast-json-stringify-compiler/-/fast-json-stringify-compiler-5.0.3.tgz",
 112 |       "integrity": "sha512-uik7yYHkLr6fxd8hJSZ8c+xF4WafPK+XzneQDPU+D10r5X19GW8lJcom2YijX2+qtFF1ENJlHXKFM9ouXNJYgQ==",
 113 |       "funding": [
 114 |         {
 115 |           "type": "github",
 116 |           "url": "https://github.com/sponsors/fastify"
 117 |         },
 118 |         {
 119 |           "type": "opencollective",
 120 |           "url": "https://opencollective.com/fastify"
 121 |         }
 122 |       ],
 123 |       "license": "MIT",
 124 |       "dependencies": {
 125 |         "fast-json-stringify": "^6.0.0"
 126 |       }
 127 |     },
 128 |     "node_modules/@fastify/forwarded": {
 129 |       "version": "3.0.0",
 130 |       "resolved": "https://registry.npmjs.org/@fastify/forwarded/-/forwarded-3.0.0.tgz",
 131 |       "integrity": "sha512-kJExsp4JCms7ipzg7SJ3y8DwmePaELHxKYtg+tZow+k0znUTf3cb+npgyqm8+ATZOdmfgfydIebPDWM172wfyA==",
 132 |       "license": "MIT"
 133 |     },
 134 |     "node_modules/@fastify/merge-json-schemas": {
 135 |       "version": "0.2.1",
 136 |       "resolved": "https://registry.npmjs.org/@fastify/merge-json-schemas/-/merge-json-schemas-0.2.1.tgz",
 137 |       "integrity": "sha512-OA3KGBCy6KtIvLf8DINC5880o5iBlDX4SxzLQS8HorJAbqluzLRn80UXU0bxZn7UOFhFgpRJDasfwn9nG4FG4A==",
 138 |       "funding": [
 139 |         {
 140 |           "type": "github",
 141 |           "url": "https://github.com/sponsors/fastify"
 142 |         },
 143 |         {
 144 |           "type": "opencollective",
 145 |           "url": "https://opencollective.com/fastify"
 146 |         }
 147 |       ],
 148 |       "license": "MIT",
 149 |       "dependencies": {
 150 |         "dequal": "^2.0.3"
 151 |       }
 152 |     },
 153 |     "node_modules/@fastify/proxy-addr": {
 154 |       "version": "5.0.0",
 155 |       "resolved": "https://registry.npmjs.org/@fastify/proxy-addr/-/proxy-addr-5.0.0.tgz",
 156 |       "integrity": "sha512-37qVVA1qZ5sgH7KpHkkC4z9SK6StIsIcOmpjvMPXNb3vx2GQxhZocogVYbr2PbbeLCQxYIPDok307xEvRZOzGA==",
 157 |       "license": "MIT",
 158 |       "dependencies": {
 159 |         "@fastify/forwarded": "^3.0.0",
 160 |         "ipaddr.js": "^2.1.0"
 161 |       }
 162 |     },
 163 |     "node_modules/@google/genai": {
 164 |       "version": "1.9.0",
 165 |       "resolved": "https://registry.npmjs.org/@google/genai/-/genai-1.9.0.tgz",
 166 |       "integrity": "sha512-w9P93OXKPMs9H1mfAx9+p3zJqQGrWBGdvK/SVc7cLZEXNHr/3+vW2eif7ZShA6wU24rNLn9z9MK2vQFUvNRI2Q==",
 167 |       "license": "Apache-2.0",
 168 |       "dependencies": {
 169 |         "google-auth-library": "^9.14.2",
 170 |         "ws": "^8.18.0"
 171 |       },
 172 |       "engines": {
 173 |         "node": ">=20.0.0"
 174 |       },
 175 |       "peerDependencies": {
 176 |         "@modelcontextprotocol/sdk": "^1.11.0"
 177 |       },
 178 |       "peerDependenciesMeta": {
 179 |         "@modelcontextprotocol/sdk": {
 180 |           "optional": true
 181 |         }
 182 |       }
 183 |     },
 184 |     "node_modules/@musistudio/llms": {
 185 |       "version": "1.0.4",
 186 |       "resolved": "https://registry.npmjs.org/@musistudio/llms/-/llms-1.0.4.tgz",
 187 |       "integrity": "sha512-z+Ge5NOaafIvgnGiZqySSz8b2sYIvRQRCVZHZH/IjotS2uQWXespcdIUu0h72toTRkLu7hVIxLuY5Poh+6PeTQ==",
 188 |       "license": "MIT",
 189 |       "dependencies": {
 190 |         "@anthropic-ai/sdk": "^0.54.0",
 191 |         "@fastify/cors": "^11.0.1",
 192 |         "@google/genai": "^1.7.0",
 193 |         "dotenv": "^16.5.0",
 194 |         "fastify": "^5.4.0",
 195 |         "openai": "^5.6.0",
 196 |         "undici": "^7.10.0"
 197 |       }
 198 |     },
 199 |     "node_modules/@nodelib/fs.scandir": {
 200 |       "version": "2.1.5",
 201 |       "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
 202 |       "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
 203 |       "dev": true,
 204 |       "license": "MIT",
 205 |       "dependencies": {
 206 |         "@nodelib/fs.stat": "2.0.5",
 207 |         "run-parallel": "^1.1.9"
 208 |       },
 209 |       "engines": {
 210 |         "node": ">= 8"
 211 |       }
 212 |     },
 213 |     "node_modules/@nodelib/fs.stat": {
 214 |       "version": "2.0.5",
 215 |       "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
 216 |       "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
 217 |       "dev": true,
 218 |       "license": "MIT",
 219 |       "engines": {
 220 |         "node": ">= 8"
 221 |       }
 222 |     },
 223 |     "node_modules/@nodelib/fs.walk": {
 224 |       "version": "1.2.8",
 225 |       "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
 226 |       "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
 227 |       "dev": true,
 228 |       "license": "MIT",
 229 |       "dependencies": {
 230 |         "@nodelib/fs.scandir": "2.1.5",
 231 |         "fastq": "^1.6.0"
 232 |       },
 233 |       "engines": {
 234 |         "node": ">= 8"
 235 |       }
 236 |     },
 237 |     "node_modules/abstract-logging": {
 238 |       "version": "2.0.1",
 239 |       "resolved": "https://registry.npmjs.org/abstract-logging/-/abstract-logging-2.0.1.tgz",
 240 |       "integrity": "sha512-2BjRTZxTPvheOvGbBslFSYOUkr+SjPtOnrLP33f+VIWLzezQpZcqVg7ja3L4dBXmzzgwT+a029jRx5PCi3JuiA==",
 241 |       "license": "MIT"
 242 |     },
 243 |     "node_modules/agent-base": {
 244 |       "version": "7.1.4",
 245 |       "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz",
 246 |       "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==",
 247 |       "license": "MIT",
 248 |       "engines": {
 249 |         "node": ">= 14"
 250 |       }
 251 |     },
 252 |     "node_modules/ajv": {
 253 |       "version": "8.17.1",
 254 |       "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz",
 255 |       "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
 256 |       "license": "MIT",
 257 |       "dependencies": {
 258 |         "fast-deep-equal": "^3.1.3",
 259 |         "fast-uri": "^3.0.1",
 260 |         "json-schema-traverse": "^1.0.0",
 261 |         "require-from-string": "^2.0.2"
 262 |       },
 263 |       "funding": {
 264 |         "type": "github",
 265 |         "url": "https://github.com/sponsors/epoberezkin"
 266 |       }
 267 |     },
 268 |     "node_modules/ajv-formats": {
 269 |       "version": "3.0.1",
 270 |       "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz",
 271 |       "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==",
 272 |       "license": "MIT",
 273 |       "dependencies": {
 274 |         "ajv": "^8.0.0"
 275 |       },
 276 |       "peerDependencies": {
 277 |         "ajv": "^8.0.0"
 278 |       },
 279 |       "peerDependenciesMeta": {
 280 |         "ajv": {
 281 |           "optional": true
 282 |         }
 283 |       }
 284 |     },
 285 |     "node_modules/atomic-sleep": {
 286 |       "version": "1.0.0",
 287 |       "resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz",
 288 |       "integrity": "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==",
 289 |       "license": "MIT",
 290 |       "engines": {
 291 |         "node": ">=8.0.0"
 292 |       }
 293 |     },
 294 |     "node_modules/avvio": {
 295 |       "version": "9.1.0",
 296 |       "resolved": "https://registry.npmjs.org/avvio/-/avvio-9.1.0.tgz",
 297 |       "integrity": "sha512-fYASnYi600CsH/j9EQov7lECAniYiBFiiAtBNuZYLA2leLe9qOvZzqYHFjtIj6gD2VMoMLP14834LFWvr4IfDw==",
 298 |       "license": "MIT",
 299 |       "dependencies": {
 300 |         "@fastify/error": "^4.0.0",
 301 |         "fastq": "^1.17.1"
 302 |       }
 303 |     },
 304 |     "node_modules/base64-js": {
 305 |       "version": "1.5.1",
 306 |       "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
 307 |       "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
 308 |       "funding": [
 309 |         {
 310 |           "type": "github",
 311 |           "url": "https://github.com/sponsors/feross"
 312 |         },
 313 |         {
 314 |           "type": "patreon",
 315 |           "url": "https://www.patreon.com/feross"
 316 |         },
 317 |         {
 318 |           "type": "consulting",
 319 |           "url": "https://feross.org/support"
 320 |         }
 321 |       ],
 322 |       "license": "MIT"
 323 |     },
 324 |     "node_modules/bignumber.js": {
 325 |       "version": "9.3.1",
 326 |       "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.3.1.tgz",
 327 |       "integrity": "sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==",
 328 |       "license": "MIT",
 329 |       "engines": {
 330 |         "node": "*"
 331 |       }
 332 |     },
 333 |     "node_modules/braces": {
 334 |       "version": "3.0.3",
 335 |       "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
 336 |       "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
 337 |       "dev": true,
 338 |       "license": "MIT",
 339 |       "dependencies": {
 340 |         "fill-range": "^7.1.1"
 341 |       },
 342 |       "engines": {
 343 |         "node": ">=8"
 344 |       }
 345 |     },
 346 |     "node_modules/buffer-equal-constant-time": {
 347 |       "version": "1.0.1",
 348 |       "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
 349 |       "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==",
 350 |       "license": "BSD-3-Clause"
 351 |     },
 352 |     "node_modules/cookie": {
 353 |       "version": "1.0.2",
 354 |       "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.0.2.tgz",
 355 |       "integrity": "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==",
 356 |       "license": "MIT",
 357 |       "engines": {
 358 |         "node": ">=18"
 359 |       }
 360 |     },
 361 |     "node_modules/cross-spawn": {
 362 |       "version": "6.0.6",
 363 |       "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.6.tgz",
 364 |       "integrity": "sha512-VqCUuhcd1iB+dsv8gxPttb5iZh/D0iubSP21g36KXdEuf6I5JiioesUVjpCdHV9MZRUfVFlvwtIUyPfxo5trtw==",
 365 |       "dev": true,
 366 |       "license": "MIT",
 367 |       "dependencies": {
 368 |         "nice-try": "^1.0.4",
 369 |         "path-key": "^2.0.1",
 370 |         "semver": "^5.5.0",
 371 |         "shebang-command": "^1.2.0",
 372 |         "which": "^1.2.9"
 373 |       },
 374 |       "engines": {
 375 |         "node": ">=4.8"
 376 |       }
 377 |     },
 378 |     "node_modules/cross-spawn/node_modules/semver": {
 379 |       "version": "5.7.2",
 380 |       "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz",
 381 |       "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==",
 382 |       "dev": true,
 383 |       "license": "ISC",
 384 |       "bin": {
 385 |         "semver": "bin/semver"
 386 |       }
 387 |     },
 388 |     "node_modules/debug": {
 389 |       "version": "4.4.1",
 390 |       "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz",
 391 |       "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==",
 392 |       "license": "MIT",
 393 |       "dependencies": {
 394 |         "ms": "^2.1.3"
 395 |       },
 396 |       "engines": {
 397 |         "node": ">=6.0"
 398 |       },
 399 |       "peerDependenciesMeta": {
 400 |         "supports-color": {
 401 |           "optional": true
 402 |         }
 403 |       }
 404 |     },
 405 |     "node_modules/dequal": {
 406 |       "version": "2.0.3",
 407 |       "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz",
 408 |       "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==",
 409 |       "license": "MIT",
 410 |       "engines": {
 411 |         "node": ">=6"
 412 |       }
 413 |     },
 414 |     "node_modules/dotenv": {
 415 |       "version": "16.5.0",
 416 |       "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.5.0.tgz",
 417 |       "integrity": "sha512-m/C+AwOAr9/W1UOIZUo232ejMNnJAJtYQjUbHoNTBNTJSvqzzDh7vnrei3o3r3m9blf6ZoDkvcw0VmozNRFJxg==",
 418 |       "license": "BSD-2-Clause",
 419 |       "engines": {
 420 |         "node": ">=12"
 421 |       },
 422 |       "funding": {
 423 |         "url": "https://dotenvx.com"
 424 |       }
 425 |     },
 426 |     "node_modules/ecdsa-sig-formatter": {
 427 |       "version": "1.0.11",
 428 |       "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
 429 |       "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==",
 430 |       "license": "Apache-2.0",
 431 |       "dependencies": {
 432 |         "safe-buffer": "^5.0.1"
 433 |       }
 434 |     },
 435 |     "node_modules/end-of-stream": {
 436 |       "version": "1.4.5",
 437 |       "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz",
 438 |       "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==",
 439 |       "dev": true,
 440 |       "license": "MIT",
 441 |       "dependencies": {
 442 |         "once": "^1.4.0"
 443 |       }
 444 |     },
 445 |     "node_modules/esbuild": {
 446 |       "version": "0.25.5",
 447 |       "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.5.tgz",
 448 |       "integrity": "sha512-P8OtKZRv/5J5hhz0cUAdu/cLuPIKXpQl1R9pZtvmHWQvrAUVd0UNIPT4IB4W3rNOqVO0rlqHmCIbSwxh/c9yUQ==",
 449 |       "dev": true,
 450 |       "hasInstallScript": true,
 451 |       "license": "MIT",
 452 |       "bin": {
 453 |         "esbuild": "bin/esbuild"
 454 |       },
 455 |       "engines": {
 456 |         "node": ">=18"
 457 |       },
 458 |       "optionalDependencies": {
 459 |         "@esbuild/aix-ppc64": "0.25.5",
 460 |         "@esbuild/android-arm": "0.25.5",
 461 |         "@esbuild/android-arm64": "0.25.5",
 462 |         "@esbuild/android-x64": "0.25.5",
 463 |         "@esbuild/darwin-arm64": "0.25.5",
 464 |         "@esbuild/darwin-x64": "0.25.5",
 465 |         "@esbuild/freebsd-arm64": "0.25.5",
 466 |         "@esbuild/freebsd-x64": "0.25.5",
 467 |         "@esbuild/linux-arm": "0.25.5",
 468 |         "@esbuild/linux-arm64": "0.25.5",
 469 |         "@esbuild/linux-ia32": "0.25.5",
 470 |         "@esbuild/linux-loong64": "0.25.5",
 471 |         "@esbuild/linux-mips64el": "0.25.5",
 472 |         "@esbuild/linux-ppc64": "0.25.5",
 473 |         "@esbuild/linux-riscv64": "0.25.5",
 474 |         "@esbuild/linux-s390x": "0.25.5",
 475 |         "@esbuild/linux-x64": "0.25.5",
 476 |         "@esbuild/netbsd-arm64": "0.25.5",
 477 |         "@esbuild/netbsd-x64": "0.25.5",
 478 |         "@esbuild/openbsd-arm64": "0.25.5",
 479 |         "@esbuild/openbsd-x64": "0.25.5",
 480 |         "@esbuild/sunos-x64": "0.25.5",
 481 |         "@esbuild/win32-arm64": "0.25.5",
 482 |         "@esbuild/win32-ia32": "0.25.5",
 483 |         "@esbuild/win32-x64": "0.25.5"
 484 |       }
 485 |     },
 486 |     "node_modules/execa": {
 487 |       "version": "1.0.0",
 488 |       "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz",
 489 |       "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==",
 490 |       "dev": true,
 491 |       "license": "MIT",
 492 |       "dependencies": {
 493 |         "cross-spawn": "^6.0.0",
 494 |         "get-stream": "^4.0.0",
 495 |         "is-stream": "^1.1.0",
 496 |         "npm-run-path": "^2.0.0",
 497 |         "p-finally": "^1.0.0",
 498 |         "signal-exit": "^3.0.0",
 499 |         "strip-eof": "^1.0.0"
 500 |       },
 501 |       "engines": {
 502 |         "node": ">=6"
 503 |       }
 504 |     },
 505 |     "node_modules/execa/node_modules/is-stream": {
 506 |       "version": "1.1.0",
 507 |       "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz",
 508 |       "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==",
 509 |       "dev": true,
 510 |       "license": "MIT",
 511 |       "engines": {
 512 |         "node": ">=0.10.0"
 513 |       }
 514 |     },
 515 |     "node_modules/extend": {
 516 |       "version": "3.0.2",
 517 |       "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
 518 |       "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==",
 519 |       "license": "MIT"
 520 |     },
 521 |     "node_modules/fast-decode-uri-component": {
 522 |       "version": "1.0.1",
 523 |       "resolved": "https://registry.npmjs.org/fast-decode-uri-component/-/fast-decode-uri-component-1.0.1.tgz",
 524 |       "integrity": "sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg==",
 525 |       "license": "MIT"
 526 |     },
 527 |     "node_modules/fast-deep-equal": {
 528 |       "version": "3.1.3",
 529 |       "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
 530 |       "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
 531 |       "license": "MIT"
 532 |     },
 533 |     "node_modules/fast-glob": {
 534 |       "version": "3.3.3",
 535 |       "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz",
 536 |       "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==",
 537 |       "dev": true,
 538 |       "license": "MIT",
 539 |       "dependencies": {
 540 |         "@nodelib/fs.stat": "^2.0.2",
 541 |         "@nodelib/fs.walk": "^1.2.3",
 542 |         "glob-parent": "^5.1.2",
 543 |         "merge2": "^1.3.0",
 544 |         "micromatch": "^4.0.8"
 545 |       },
 546 |       "engines": {
 547 |         "node": ">=8.6.0"
 548 |       }
 549 |     },
 550 |     "node_modules/fast-json-stringify": {
 551 |       "version": "6.0.1",
 552 |       "resolved": "https://registry.npmjs.org/fast-json-stringify/-/fast-json-stringify-6.0.1.tgz",
 553 |       "integrity": "sha512-s7SJE83QKBZwg54dIbD5rCtzOBVD43V1ReWXXYqBgwCwHLYAAT0RQc/FmrQglXqWPpz6omtryJQOau5jI4Nrvg==",
 554 |       "funding": [
 555 |         {
 556 |           "type": "github",
 557 |           "url": "https://github.com/sponsors/fastify"
 558 |         },
 559 |         {
 560 |           "type": "opencollective",
 561 |           "url": "https://opencollective.com/fastify"
 562 |         }
 563 |       ],
 564 |       "license": "MIT",
 565 |       "dependencies": {
 566 |         "@fastify/merge-json-schemas": "^0.2.0",
 567 |         "ajv": "^8.12.0",
 568 |         "ajv-formats": "^3.0.1",
 569 |         "fast-uri": "^3.0.0",
 570 |         "json-schema-ref-resolver": "^2.0.0",
 571 |         "rfdc": "^1.2.0"
 572 |       }
 573 |     },
 574 |     "node_modules/fast-querystring": {
 575 |       "version": "1.1.2",
 576 |       "resolved": "https://registry.npmjs.org/fast-querystring/-/fast-querystring-1.1.2.tgz",
 577 |       "integrity": "sha512-g6KuKWmFXc0fID8WWH0jit4g0AGBoJhCkJMb1RmbsSEUNvQ+ZC8D6CUZ+GtF8nMzSPXnhiePyyqqipzNNEnHjg==",
 578 |       "license": "MIT",
 579 |       "dependencies": {
 580 |         "fast-decode-uri-component": "^1.0.1"
 581 |       }
 582 |     },
 583 |     "node_modules/fast-redact": {
 584 |       "version": "3.5.0",
 585 |       "resolved": "https://registry.npmjs.org/fast-redact/-/fast-redact-3.5.0.tgz",
 586 |       "integrity": "sha512-dwsoQlS7h9hMeYUq1W++23NDcBLV4KqONnITDV9DjfS3q1SgDGVrBdvvTLUotWtPSD7asWDV9/CmsZPy8Hf70A==",
 587 |       "license": "MIT",
 588 |       "engines": {
 589 |         "node": ">=6"
 590 |       }
 591 |     },
 592 |     "node_modules/fast-uri": {
 593 |       "version": "3.0.6",
 594 |       "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.6.tgz",
 595 |       "integrity": "sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==",
 596 |       "funding": [
 597 |         {
 598 |           "type": "github",
 599 |           "url": "https://github.com/sponsors/fastify"
 600 |         },
 601 |         {
 602 |           "type": "opencollective",
 603 |           "url": "https://opencollective.com/fastify"
 604 |         }
 605 |       ],
 606 |       "license": "BSD-3-Clause"
 607 |     },
 608 |     "node_modules/fastify": {
 609 |       "version": "5.4.0",
 610 |       "resolved": "https://registry.npmjs.org/fastify/-/fastify-5.4.0.tgz",
 611 |       "integrity": "sha512-I4dVlUe+WNQAhKSyv15w+dwUh2EPiEl4X2lGYMmNSgF83WzTMAPKGdWEv5tPsCQOb+SOZwz8Vlta2vF+OeDgRw==",
 612 |       "funding": [
 613 |         {
 614 |           "type": "github",
 615 |           "url": "https://github.com/sponsors/fastify"
 616 |         },
 617 |         {
 618 |           "type": "opencollective",
 619 |           "url": "https://opencollective.com/fastify"
 620 |         }
 621 |       ],
 622 |       "license": "MIT",
 623 |       "dependencies": {
 624 |         "@fastify/ajv-compiler": "^4.0.0",
 625 |         "@fastify/error": "^4.0.0",
 626 |         "@fastify/fast-json-stringify-compiler": "^5.0.0",
 627 |         "@fastify/proxy-addr": "^5.0.0",
 628 |         "abstract-logging": "^2.0.1",
 629 |         "avvio": "^9.0.0",
 630 |         "fast-json-stringify": "^6.0.0",
 631 |         "find-my-way": "^9.0.0",
 632 |         "light-my-request": "^6.0.0",
 633 |         "pino": "^9.0.0",
 634 |         "process-warning": "^5.0.0",
 635 |         "rfdc": "^1.3.1",
 636 |         "secure-json-parse": "^4.0.0",
 637 |         "semver": "^7.6.0",
 638 |         "toad-cache": "^3.7.0"
 639 |       }
 640 |     },
 641 |     "node_modules/fastify-plugin": {
 642 |       "version": "5.0.1",
 643 |       "resolved": "https://registry.npmjs.org/fastify-plugin/-/fastify-plugin-5.0.1.tgz",
 644 |       "integrity": "sha512-HCxs+YnRaWzCl+cWRYFnHmeRFyR5GVnJTAaCJQiYzQSDwK9MgJdyAsuL3nh0EWRCYMgQ5MeziymvmAhUHYHDUQ==",
 645 |       "license": "MIT"
 646 |     },
 647 |     "node_modules/fastq": {
 648 |       "version": "1.19.1",
 649 |       "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz",
 650 |       "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==",
 651 |       "license": "ISC",
 652 |       "dependencies": {
 653 |         "reusify": "^1.0.4"
 654 |       }
 655 |     },
 656 |     "node_modules/fill-range": {
 657 |       "version": "7.1.1",
 658 |       "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
 659 |       "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
 660 |       "dev": true,
 661 |       "license": "MIT",
 662 |       "dependencies": {
 663 |         "to-regex-range": "^5.0.1"
 664 |       },
 665 |       "engines": {
 666 |         "node": ">=8"
 667 |       }
 668 |     },
 669 |     "node_modules/find-my-way": {
 670 |       "version": "9.3.0",
 671 |       "resolved": "https://registry.npmjs.org/find-my-way/-/find-my-way-9.3.0.tgz",
 672 |       "integrity": "sha512-eRoFWQw+Yv2tuYlK2pjFS2jGXSxSppAs3hSQjfxVKxM5amECzIgYYc1FEI8ZmhSh/Ig+FrKEz43NLRKJjYCZVg==",
 673 |       "license": "MIT",
 674 |       "dependencies": {
 675 |         "fast-deep-equal": "^3.1.3",
 676 |         "fast-querystring": "^1.0.0",
 677 |         "safe-regex2": "^5.0.0"
 678 |       },
 679 |       "engines": {
 680 |         "node": ">=20"
 681 |       }
 682 |     },
 683 |     "node_modules/function-bind": {
 684 |       "version": "1.1.2",
 685 |       "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
 686 |       "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
 687 |       "dev": true,
 688 |       "license": "MIT",
 689 |       "funding": {
 690 |         "url": "https://github.com/sponsors/ljharb"
 691 |       }
 692 |     },
 693 |     "node_modules/gaxios": {
 694 |       "version": "6.7.1",
 695 |       "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-6.7.1.tgz",
 696 |       "integrity": "sha512-LDODD4TMYx7XXdpwxAVRAIAuB0bzv0s+ywFonY46k126qzQHT9ygyoa9tncmOiQmmDrik65UYsEkv3lbfqQ3yQ==",
 697 |       "license": "Apache-2.0",
 698 |       "dependencies": {
 699 |         "extend": "^3.0.2",
 700 |         "https-proxy-agent": "^7.0.1",
 701 |         "is-stream": "^2.0.0",
 702 |         "node-fetch": "^2.6.9",
 703 |         "uuid": "^9.0.1"
 704 |       },
 705 |       "engines": {
 706 |         "node": ">=14"
 707 |       }
 708 |     },
 709 |     "node_modules/gaxios/node_modules/uuid": {
 710 |       "version": "9.0.1",
 711 |       "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz",
 712 |       "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==",
 713 |       "funding": [
 714 |         "https://github.com/sponsors/broofa",
 715 |         "https://github.com/sponsors/ctavan"
 716 |       ],
 717 |       "license": "MIT",
 718 |       "bin": {
 719 |         "uuid": "dist/bin/uuid"
 720 |       }
 721 |     },
 722 |     "node_modules/gcp-metadata": {
 723 |       "version": "6.1.1",
 724 |       "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-6.1.1.tgz",
 725 |       "integrity": "sha512-a4tiq7E0/5fTjxPAaH4jpjkSv/uCaU2p5KC6HVGrvl0cDjA8iBZv4vv1gyzlmK0ZUKqwpOyQMKzZQe3lTit77A==",
 726 |       "license": "Apache-2.0",
 727 |       "dependencies": {
 728 |         "gaxios": "^6.1.1",
 729 |         "google-logging-utils": "^0.0.2",
 730 |         "json-bigint": "^1.0.0"
 731 |       },
 732 |       "engines": {
 733 |         "node": ">=14"
 734 |       }
 735 |     },
 736 |     "node_modules/get-stream": {
 737 |       "version": "4.1.0",
 738 |       "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz",
 739 |       "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==",
 740 |       "dev": true,
 741 |       "license": "MIT",
 742 |       "dependencies": {
 743 |         "pump": "^3.0.0"
 744 |       },
 745 |       "engines": {
 746 |         "node": ">=6"
 747 |       }
 748 |     },
 749 |     "node_modules/glob-parent": {
 750 |       "version": "5.1.2",
 751 |       "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
 752 |       "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
 753 |       "dev": true,
 754 |       "license": "ISC",
 755 |       "dependencies": {
 756 |         "is-glob": "^4.0.1"
 757 |       },
 758 |       "engines": {
 759 |         "node": ">= 6"
 760 |       }
 761 |     },
 762 |     "node_modules/google-auth-library": {
 763 |       "version": "9.15.1",
 764 |       "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-9.15.1.tgz",
 765 |       "integrity": "sha512-Jb6Z0+nvECVz+2lzSMt9u98UsoakXxA2HGHMCxh+so3n90XgYWkq5dur19JAJV7ONiJY22yBTyJB1TSkvPq9Ng==",
 766 |       "license": "Apache-2.0",
 767 |       "dependencies": {
 768 |         "base64-js": "^1.3.0",
 769 |         "ecdsa-sig-formatter": "^1.0.11",
 770 |         "gaxios": "^6.1.1",
 771 |         "gcp-metadata": "^6.1.0",
 772 |         "gtoken": "^7.0.0",
 773 |         "jws": "^4.0.0"
 774 |       },
 775 |       "engines": {
 776 |         "node": ">=14"
 777 |       }
 778 |     },
 779 |     "node_modules/google-logging-utils": {
 780 |       "version": "0.0.2",
 781 |       "resolved": "https://registry.npmjs.org/google-logging-utils/-/google-logging-utils-0.0.2.tgz",
 782 |       "integrity": "sha512-NEgUnEcBiP5HrPzufUkBzJOD/Sxsco3rLNo1F1TNf7ieU8ryUzBhqba8r756CjLX7rn3fHl6iLEwPYuqpoKgQQ==",
 783 |       "license": "Apache-2.0",
 784 |       "engines": {
 785 |         "node": ">=14"
 786 |       }
 787 |     },
 788 |     "node_modules/gtoken": {
 789 |       "version": "7.1.0",
 790 |       "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-7.1.0.tgz",
 791 |       "integrity": "sha512-pCcEwRi+TKpMlxAQObHDQ56KawURgyAf6jtIY046fJ5tIv3zDe/LEIubckAO8fj6JnAxLdmWkUfNyulQ2iKdEw==",
 792 |       "license": "MIT",
 793 |       "dependencies": {
 794 |         "gaxios": "^6.0.0",
 795 |         "jws": "^4.0.0"
 796 |       },
 797 |       "engines": {
 798 |         "node": ">=14.0.0"
 799 |       }
 800 |     },
 801 |     "node_modules/hasown": {
 802 |       "version": "2.0.2",
 803 |       "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
 804 |       "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
 805 |       "dev": true,
 806 |       "license": "MIT",
 807 |       "dependencies": {
 808 |         "function-bind": "^1.1.2"
 809 |       },
 810 |       "engines": {
 811 |         "node": ">= 0.4"
 812 |       }
 813 |     },
 814 |     "node_modules/https-proxy-agent": {
 815 |       "version": "7.0.6",
 816 |       "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz",
 817 |       "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==",
 818 |       "license": "MIT",
 819 |       "dependencies": {
 820 |         "agent-base": "^7.1.2",
 821 |         "debug": "4"
 822 |       },
 823 |       "engines": {
 824 |         "node": ">= 14"
 825 |       }
 826 |     },
 827 |     "node_modules/interpret": {
 828 |       "version": "1.4.0",
 829 |       "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz",
 830 |       "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==",
 831 |       "dev": true,
 832 |       "license": "MIT",
 833 |       "engines": {
 834 |         "node": ">= 0.10"
 835 |       }
 836 |     },
 837 |     "node_modules/ipaddr.js": {
 838 |       "version": "2.2.0",
 839 |       "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.2.0.tgz",
 840 |       "integrity": "sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA==",
 841 |       "license": "MIT",
 842 |       "engines": {
 843 |         "node": ">= 10"
 844 |       }
 845 |     },
 846 |     "node_modules/is-core-module": {
 847 |       "version": "2.16.1",
 848 |       "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz",
 849 |       "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==",
 850 |       "dev": true,
 851 |       "license": "MIT",
 852 |       "dependencies": {
 853 |         "hasown": "^2.0.2"
 854 |       },
 855 |       "engines": {
 856 |         "node": ">= 0.4"
 857 |       },
 858 |       "funding": {
 859 |         "url": "https://github.com/sponsors/ljharb"
 860 |       }
 861 |     },
 862 |     "node_modules/is-extglob": {
 863 |       "version": "2.1.1",
 864 |       "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
 865 |       "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
 866 |       "dev": true,
 867 |       "license": "MIT",
 868 |       "engines": {
 869 |         "node": ">=0.10.0"
 870 |       }
 871 |     },
 872 |     "node_modules/is-glob": {
 873 |       "version": "4.0.3",
 874 |       "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
 875 |       "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
 876 |       "dev": true,
 877 |       "license": "MIT",
 878 |       "dependencies": {
 879 |         "is-extglob": "^2.1.1"
 880 |       },
 881 |       "engines": {
 882 |         "node": ">=0.10.0"
 883 |       }
 884 |     },
 885 |     "node_modules/is-number": {
 886 |       "version": "7.0.0",
 887 |       "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
 888 |       "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
 889 |       "dev": true,
 890 |       "license": "MIT",
 891 |       "engines": {
 892 |         "node": ">=0.12.0"
 893 |       }
 894 |     },
 895 |     "node_modules/is-stream": {
 896 |       "version": "2.0.1",
 897 |       "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
 898 |       "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==",
 899 |       "license": "MIT",
 900 |       "engines": {
 901 |         "node": ">=8"
 902 |       },
 903 |       "funding": {
 904 |         "url": "https://github.com/sponsors/sindresorhus"
 905 |       }
 906 |     },
 907 |     "node_modules/isexe": {
 908 |       "version": "2.0.0",
 909 |       "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
 910 |       "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
 911 |       "dev": true,
 912 |       "license": "ISC"
 913 |     },
 914 |     "node_modules/json-bigint": {
 915 |       "version": "1.0.0",
 916 |       "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz",
 917 |       "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==",
 918 |       "license": "MIT",
 919 |       "dependencies": {
 920 |         "bignumber.js": "^9.0.0"
 921 |       }
 922 |     },
 923 |     "node_modules/json-schema-ref-resolver": {
 924 |       "version": "2.0.1",
 925 |       "resolved": "https://registry.npmjs.org/json-schema-ref-resolver/-/json-schema-ref-resolver-2.0.1.tgz",
 926 |       "integrity": "sha512-HG0SIB9X4J8bwbxCbnd5FfPEbcXAJYTi1pBJeP/QPON+w8ovSME8iRG+ElHNxZNX2Qh6eYn1GdzJFS4cDFfx0Q==",
 927 |       "funding": [
 928 |         {
 929 |           "type": "github",
 930 |           "url": "https://github.com/sponsors/fastify"
 931 |         },
 932 |         {
 933 |           "type": "opencollective",
 934 |           "url": "https://opencollective.com/fastify"
 935 |         }
 936 |       ],
 937 |       "license": "MIT",
 938 |       "dependencies": {
 939 |         "dequal": "^2.0.3"
 940 |       }
 941 |     },
 942 |     "node_modules/json-schema-traverse": {
 943 |       "version": "1.0.0",
 944 |       "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
 945 |       "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
 946 |       "license": "MIT"
 947 |     },
 948 |     "node_modules/jwa": {
 949 |       "version": "2.0.1",
 950 |       "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz",
 951 |       "integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==",
 952 |       "license": "MIT",
 953 |       "dependencies": {
 954 |         "buffer-equal-constant-time": "^1.0.1",
 955 |         "ecdsa-sig-formatter": "1.0.11",
 956 |         "safe-buffer": "^5.0.1"
 957 |       }
 958 |     },
 959 |     "node_modules/jws": {
 960 |       "version": "4.0.0",
 961 |       "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz",
 962 |       "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==",
 963 |       "license": "MIT",
 964 |       "dependencies": {
 965 |         "jwa": "^2.0.0",
 966 |         "safe-buffer": "^5.0.1"
 967 |       }
 968 |     },
 969 |     "node_modules/light-my-request": {
 970 |       "version": "6.6.0",
 971 |       "resolved": "https://registry.npmjs.org/light-my-request/-/light-my-request-6.6.0.tgz",
 972 |       "integrity": "sha512-CHYbu8RtboSIoVsHZ6Ye4cj4Aw/yg2oAFimlF7mNvfDV192LR7nDiKtSIfCuLT7KokPSTn/9kfVLm5OGN0A28A==",
 973 |       "funding": [
 974 |         {
 975 |           "type": "github",
 976 |           "url": "https://github.com/sponsors/fastify"
 977 |         },
 978 |         {
 979 |           "type": "opencollective",
 980 |           "url": "https://opencollective.com/fastify"
 981 |         }
 982 |       ],
 983 |       "license": "BSD-3-Clause",
 984 |       "dependencies": {
 985 |         "cookie": "^1.0.1",
 986 |         "process-warning": "^4.0.0",
 987 |         "set-cookie-parser": "^2.6.0"
 988 |       }
 989 |     },
 990 |     "node_modules/light-my-request/node_modules/process-warning": {
 991 |       "version": "4.0.1",
 992 |       "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-4.0.1.tgz",
 993 |       "integrity": "sha512-3c2LzQ3rY9d0hc1emcsHhfT9Jwz0cChib/QN89oME2R451w5fy3f0afAhERFZAwrbDU43wk12d0ORBpDVME50Q==",
 994 |       "funding": [
 995 |         {
 996 |           "type": "github",
 997 |           "url": "https://github.com/sponsors/fastify"
 998 |         },
 999 |         {
1000 |           "type": "opencollective",
1001 |           "url": "https://opencollective.com/fastify"
1002 |         }
1003 |       ],
1004 |       "license": "MIT"
1005 |     },
1006 |     "node_modules/merge2": {
1007 |       "version": "1.4.1",
1008 |       "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
1009 |       "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
1010 |       "dev": true,
1011 |       "license": "MIT",
1012 |       "engines": {
1013 |         "node": ">= 8"
1014 |       }
1015 |     },
1016 |     "node_modules/micromatch": {
1017 |       "version": "4.0.8",
1018 |       "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
1019 |       "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
1020 |       "dev": true,
1021 |       "license": "MIT",
1022 |       "dependencies": {
1023 |         "braces": "^3.0.3",
1024 |         "picomatch": "^2.3.1"
1025 |       },
1026 |       "engines": {
1027 |         "node": ">=8.6"
1028 |       }
1029 |     },
1030 |     "node_modules/minimist": {
1031 |       "version": "1.2.8",
1032 |       "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
1033 |       "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
1034 |       "dev": true,
1035 |       "license": "MIT",
1036 |       "funding": {
1037 |         "url": "https://github.com/sponsors/ljharb"
1038 |       }
1039 |     },
1040 |     "node_modules/ms": {
1041 |       "version": "2.1.3",
1042 |       "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
1043 |       "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
1044 |       "license": "MIT"
1045 |     },
1046 |     "node_modules/nice-try": {
1047 |       "version": "1.0.5",
1048 |       "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz",
1049 |       "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==",
1050 |       "dev": true,
1051 |       "license": "MIT"
1052 |     },
1053 |     "node_modules/node-fetch": {
1054 |       "version": "2.7.0",
1055 |       "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
1056 |       "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==",
1057 |       "license": "MIT",
1058 |       "dependencies": {
1059 |         "whatwg-url": "^5.0.0"
1060 |       },
1061 |       "engines": {
1062 |         "node": "4.x || >=6.0.0"
1063 |       },
1064 |       "peerDependencies": {
1065 |         "encoding": "^0.1.0"
1066 |       },
1067 |       "peerDependenciesMeta": {
1068 |         "encoding": {
1069 |           "optional": true
1070 |         }
1071 |       }
1072 |     },
1073 |     "node_modules/npm-run-path": {
1074 |       "version": "2.0.2",
1075 |       "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz",
1076 |       "integrity": "sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==",
1077 |       "dev": true,
1078 |       "license": "MIT",
1079 |       "dependencies": {
1080 |         "path-key": "^2.0.0"
1081 |       },
1082 |       "engines": {
1083 |         "node": ">=4"
1084 |       }
1085 |     },
1086 |     "node_modules/on-exit-leak-free": {
1087 |       "version": "2.1.2",
1088 |       "resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-2.1.2.tgz",
1089 |       "integrity": "sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==",
1090 |       "license": "MIT",
1091 |       "engines": {
1092 |         "node": ">=14.0.0"
1093 |       }
1094 |     },
1095 |     "node_modules/once": {
1096 |       "version": "1.4.0",
1097 |       "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
1098 |       "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
1099 |       "dev": true,
1100 |       "license": "ISC",
1101 |       "dependencies": {
1102 |         "wrappy": "1"
1103 |       }
1104 |     },
1105 |     "node_modules/openai": {
1106 |       "version": "5.9.0",
1107 |       "resolved": "https://registry.npmjs.org/openai/-/openai-5.9.0.tgz",
1108 |       "integrity": "sha512-cmLC0pfqLLhBGxE4aZPyRPjydgYCncppV2ClQkKmW79hNjCvmzkfhz8rN5/YVDmjVQlFV+UsF1JIuNjNgeagyQ==",
1109 |       "license": "Apache-2.0",
1110 |       "bin": {
1111 |         "openai": "bin/cli"
1112 |       },
1113 |       "peerDependencies": {
1114 |         "ws": "^8.18.0",
1115 |         "zod": "^3.23.8"
1116 |       },
1117 |       "peerDependenciesMeta": {
1118 |         "ws": {
1119 |           "optional": true
1120 |         },
1121 |         "zod": {
1122 |           "optional": true
1123 |         }
1124 |       }
1125 |     },
1126 |     "node_modules/p-finally": {
1127 |       "version": "1.0.0",
1128 |       "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz",
1129 |       "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==",
1130 |       "dev": true,
1131 |       "license": "MIT",
1132 |       "engines": {
1133 |         "node": ">=4"
1134 |       }
1135 |     },
1136 |     "node_modules/path-key": {
1137 |       "version": "2.0.1",
1138 |       "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz",
1139 |       "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==",
1140 |       "dev": true,
1141 |       "license": "MIT",
1142 |       "engines": {
1143 |         "node": ">=4"
1144 |       }
1145 |     },
1146 |     "node_modules/path-parse": {
1147 |       "version": "1.0.7",
1148 |       "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
1149 |       "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
1150 |       "dev": true,
1151 |       "license": "MIT"
1152 |     },
1153 |     "node_modules/picomatch": {
1154 |       "version": "2.3.1",
1155 |       "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
1156 |       "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
1157 |       "dev": true,
1158 |       "license": "MIT",
1159 |       "engines": {
1160 |         "node": ">=8.6"
1161 |       },
1162 |       "funding": {
1163 |         "url": "https://github.com/sponsors/jonschlinkert"
1164 |       }
1165 |     },
1166 |     "node_modules/pino": {
1167 |       "version": "9.7.0",
1168 |       "resolved": "https://registry.npmjs.org/pino/-/pino-9.7.0.tgz",
1169 |       "integrity": "sha512-vnMCM6xZTb1WDmLvtG2lE/2p+t9hDEIvTWJsu6FejkE62vB7gDhvzrpFR4Cw2to+9JNQxVnkAKVPA1KPB98vWg==",
1170 |       "license": "MIT",
1171 |       "dependencies": {
1172 |         "atomic-sleep": "^1.0.0",
1173 |         "fast-redact": "^3.1.1",
1174 |         "on-exit-leak-free": "^2.1.0",
1175 |         "pino-abstract-transport": "^2.0.0",
1176 |         "pino-std-serializers": "^7.0.0",
1177 |         "process-warning": "^5.0.0",
1178 |         "quick-format-unescaped": "^4.0.3",
1179 |         "real-require": "^0.2.0",
1180 |         "safe-stable-stringify": "^2.3.1",
1181 |         "sonic-boom": "^4.0.1",
1182 |         "thread-stream": "^3.0.0"
1183 |       },
1184 |       "bin": {
1185 |         "pino": "bin.js"
1186 |       }
1187 |     },
1188 |     "node_modules/pino-abstract-transport": {
1189 |       "version": "2.0.0",
1190 |       "resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-2.0.0.tgz",
1191 |       "integrity": "sha512-F63x5tizV6WCh4R6RHyi2Ml+M70DNRXt/+HANowMflpgGFMAym/VKm6G7ZOQRjqN7XbGxK1Lg9t6ZrtzOaivMw==",
1192 |       "license": "MIT",
1193 |       "dependencies": {
1194 |         "split2": "^4.0.0"
1195 |       }
1196 |     },
1197 |     "node_modules/pino-std-serializers": {
1198 |       "version": "7.0.0",
1199 |       "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-7.0.0.tgz",
1200 |       "integrity": "sha512-e906FRY0+tV27iq4juKzSYPbUj2do2X2JX4EzSca1631EB2QJQUqGbDuERal7LCtOpxl6x3+nvo9NPZcmjkiFA==",
1201 |       "license": "MIT"
1202 |     },
1203 |     "node_modules/process-warning": {
1204 |       "version": "5.0.0",
1205 |       "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-5.0.0.tgz",
1206 |       "integrity": "sha512-a39t9ApHNx2L4+HBnQKqxxHNs1r7KF+Intd8Q/g1bUh6q0WIp9voPXJ/x0j+ZL45KF1pJd9+q2jLIRMfvEshkA==",
1207 |       "funding": [
1208 |         {
1209 |           "type": "github",
1210 |           "url": "https://github.com/sponsors/fastify"
1211 |         },
1212 |         {
1213 |           "type": "opencollective",
1214 |           "url": "https://opencollective.com/fastify"
1215 |         }
1216 |       ],
1217 |       "license": "MIT"
1218 |     },
1219 |     "node_modules/pump": {
1220 |       "version": "3.0.3",
1221 |       "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz",
1222 |       "integrity": "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==",
1223 |       "dev": true,
1224 |       "license": "MIT",
1225 |       "dependencies": {
1226 |         "end-of-stream": "^1.1.0",
1227 |         "once": "^1.3.1"
1228 |       }
1229 |     },
1230 |     "node_modules/queue-microtask": {
1231 |       "version": "1.2.3",
1232 |       "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
1233 |       "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
1234 |       "dev": true,
1235 |       "funding": [
1236 |         {
1237 |           "type": "github",
1238 |           "url": "https://github.com/sponsors/feross"
1239 |         },
1240 |         {
1241 |           "type": "patreon",
1242 |           "url": "https://www.patreon.com/feross"
1243 |         },
1244 |         {
1245 |           "type": "consulting",
1246 |           "url": "https://feross.org/support"
1247 |         }
1248 |       ],
1249 |       "license": "MIT"
1250 |     },
1251 |     "node_modules/quick-format-unescaped": {
1252 |       "version": "4.0.4",
1253 |       "resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz",
1254 |       "integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==",
1255 |       "license": "MIT"
1256 |     },
1257 |     "node_modules/real-require": {
1258 |       "version": "0.2.0",
1259 |       "resolved": "https://registry.npmjs.org/real-require/-/real-require-0.2.0.tgz",
1260 |       "integrity": "sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==",
1261 |       "license": "MIT",
1262 |       "engines": {
1263 |         "node": ">= 12.13.0"
1264 |       }
1265 |     },
1266 |     "node_modules/rechoir": {
1267 |       "version": "0.6.2",
1268 |       "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz",
1269 |       "integrity": "sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==",
1270 |       "dev": true,
1271 |       "dependencies": {
1272 |         "resolve": "^1.1.6"
1273 |       },
1274 |       "engines": {
1275 |         "node": ">= 0.10"
1276 |       }
1277 |     },
1278 |     "node_modules/require-from-string": {
1279 |       "version": "2.0.2",
1280 |       "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
1281 |       "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
1282 |       "license": "MIT",
1283 |       "engines": {
1284 |         "node": ">=0.10.0"
1285 |       }
1286 |     },
1287 |     "node_modules/resolve": {
1288 |       "version": "1.22.10",
1289 |       "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz",
1290 |       "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==",
1291 |       "dev": true,
1292 |       "license": "MIT",
1293 |       "dependencies": {
1294 |         "is-core-module": "^2.16.0",
1295 |         "path-parse": "^1.0.7",
1296 |         "supports-preserve-symlinks-flag": "^1.0.0"
1297 |       },
1298 |       "bin": {
1299 |         "resolve": "bin/resolve"
1300 |       },
1301 |       "engines": {
1302 |         "node": ">= 0.4"
1303 |       },
1304 |       "funding": {
1305 |         "url": "https://github.com/sponsors/ljharb"
1306 |       }
1307 |     },
1308 |     "node_modules/ret": {
1309 |       "version": "0.5.0",
1310 |       "resolved": "https://registry.npmjs.org/ret/-/ret-0.5.0.tgz",
1311 |       "integrity": "sha512-I1XxrZSQ+oErkRR4jYbAyEEu2I0avBvvMM5JN+6EBprOGRCs63ENqZ3vjavq8fBw2+62G5LF5XelKwuJpcvcxw==",
1312 |       "license": "MIT",
1313 |       "engines": {
1314 |         "node": ">=10"
1315 |       }
1316 |     },
1317 |     "node_modules/reusify": {
1318 |       "version": "1.1.0",
1319 |       "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz",
1320 |       "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==",
1321 |       "license": "MIT",
1322 |       "engines": {
1323 |         "iojs": ">=1.0.0",
1324 |         "node": ">=0.10.0"
1325 |       }
1326 |     },
1327 |     "node_modules/rfdc": {
1328 |       "version": "1.4.1",
1329 |       "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz",
1330 |       "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==",
1331 |       "license": "MIT"
1332 |     },
1333 |     "node_modules/run-parallel": {
1334 |       "version": "1.2.0",
1335 |       "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
1336 |       "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
1337 |       "dev": true,
1338 |       "funding": [
1339 |         {
1340 |           "type": "github",
1341 |           "url": "https://github.com/sponsors/feross"
1342 |         },
1343 |         {
1344 |           "type": "patreon",
1345 |           "url": "https://www.patreon.com/feross"
1346 |         },
1347 |         {
1348 |           "type": "consulting",
1349 |           "url": "https://feross.org/support"
1350 |         }
1351 |       ],
1352 |       "license": "MIT",
1353 |       "dependencies": {
1354 |         "queue-microtask": "^1.2.2"
1355 |       }
1356 |     },
1357 |     "node_modules/safe-buffer": {
1358 |       "version": "5.2.1",
1359 |       "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
1360 |       "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
1361 |       "funding": [
1362 |         {
1363 |           "type": "github",
1364 |           "url": "https://github.com/sponsors/feross"
1365 |         },
1366 |         {
1367 |           "type": "patreon",
1368 |           "url": "https://www.patreon.com/feross"
1369 |         },
1370 |         {
1371 |           "type": "consulting",
1372 |           "url": "https://feross.org/support"
1373 |         }
1374 |       ],
1375 |       "license": "MIT"
1376 |     },
1377 |     "node_modules/safe-regex2": {
1378 |       "version": "5.0.0",
1379 |       "resolved": "https://registry.npmjs.org/safe-regex2/-/safe-regex2-5.0.0.tgz",
1380 |       "integrity": "sha512-YwJwe5a51WlK7KbOJREPdjNrpViQBI3p4T50lfwPuDhZnE3XGVTlGvi+aolc5+RvxDD6bnUmjVsU9n1eboLUYw==",
1381 |       "funding": [
1382 |         {
1383 |           "type": "github",
1384 |           "url": "https://github.com/sponsors/fastify"
1385 |         },
1386 |         {
1387 |           "type": "opencollective",
1388 |           "url": "https://opencollective.com/fastify"
1389 |         }
1390 |       ],
1391 |       "license": "MIT",
1392 |       "dependencies": {
1393 |         "ret": "~0.5.0"
1394 |       }
1395 |     },
1396 |     "node_modules/safe-stable-stringify": {
1397 |       "version": "2.5.0",
1398 |       "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz",
1399 |       "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==",
1400 |       "license": "MIT",
1401 |       "engines": {
1402 |         "node": ">=10"
1403 |       }
1404 |     },
1405 |     "node_modules/secure-json-parse": {
1406 |       "version": "4.0.0",
1407 |       "resolved": "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-4.0.0.tgz",
1408 |       "integrity": "sha512-dxtLJO6sc35jWidmLxo7ij+Eg48PM/kleBsxpC8QJE0qJICe+KawkDQmvCMZUr9u7WKVHgMW6vy3fQ7zMiFZMA==",
1409 |       "funding": [
1410 |         {
1411 |           "type": "github",
1412 |           "url": "https://github.com/sponsors/fastify"
1413 |         },
1414 |         {
1415 |           "type": "opencollective",
1416 |           "url": "https://opencollective.com/fastify"
1417 |         }
1418 |       ],
1419 |       "license": "BSD-3-Clause"
1420 |     },
1421 |     "node_modules/semver": {
1422 |       "version": "7.7.2",
1423 |       "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
1424 |       "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
1425 |       "license": "ISC",
1426 |       "bin": {
1427 |         "semver": "bin/semver.js"
1428 |       },
1429 |       "engines": {
1430 |         "node": ">=10"
1431 |       }
1432 |     },
1433 |     "node_modules/set-cookie-parser": {
1434 |       "version": "2.7.1",
1435 |       "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.1.tgz",
1436 |       "integrity": "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==",
1437 |       "license": "MIT"
1438 |     },
1439 |     "node_modules/shebang-command": {
1440 |       "version": "1.2.0",
1441 |       "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",
1442 |       "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==",
1443 |       "dev": true,
1444 |       "license": "MIT",
1445 |       "dependencies": {
1446 |         "shebang-regex": "^1.0.0"
1447 |       },
1448 |       "engines": {
1449 |         "node": ">=0.10.0"
1450 |       }
1451 |     },
1452 |     "node_modules/shebang-regex": {
1453 |       "version": "1.0.0",
1454 |       "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz",
1455 |       "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==",
1456 |       "dev": true,
1457 |       "license": "MIT",
1458 |       "engines": {
1459 |         "node": ">=0.10.0"
1460 |       }
1461 |     },
1462 |     "node_modules/shelljs": {
1463 |       "version": "0.9.2",
1464 |       "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.9.2.tgz",
1465 |       "integrity": "sha512-S3I64fEiKgTZzKCC46zT/Ib9meqofLrQVbpSswtjFfAVDW+AZ54WTnAM/3/yENoxz/V1Cy6u3kiiEbQ4DNphvw==",
1466 |       "dev": true,
1467 |       "license": "BSD-3-Clause",
1468 |       "dependencies": {
1469 |         "execa": "^1.0.0",
1470 |         "fast-glob": "^3.3.2",
1471 |         "interpret": "^1.0.0",
1472 |         "rechoir": "^0.6.2"
1473 |       },
1474 |       "bin": {
1475 |         "shjs": "bin/shjs"
1476 |       },
1477 |       "engines": {
1478 |         "node": ">=18"
1479 |       }
1480 |     },
1481 |     "node_modules/shx": {
1482 |       "version": "0.4.0",
1483 |       "resolved": "https://registry.npmjs.org/shx/-/shx-0.4.0.tgz",
1484 |       "integrity": "sha512-Z0KixSIlGPpijKgcH6oCMCbltPImvaKy0sGH8AkLRXw1KyzpKtaCTizP2xen+hNDqVF4xxgvA0KXSb9o4Q6hnA==",
1485 |       "dev": true,
1486 |       "license": "MIT",
1487 |       "dependencies": {
1488 |         "minimist": "^1.2.8",
1489 |         "shelljs": "^0.9.2"
1490 |       },
1491 |       "bin": {
1492 |         "shx": "lib/cli.js"
1493 |       },
1494 |       "engines": {
1495 |         "node": ">=18"
1496 |       }
1497 |     },
1498 |     "node_modules/signal-exit": {
1499 |       "version": "3.0.7",
1500 |       "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
1501 |       "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
1502 |       "dev": true,
1503 |       "license": "ISC"
1504 |     },
1505 |     "node_modules/sonic-boom": {
1506 |       "version": "4.2.0",
1507 |       "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-4.2.0.tgz",
1508 |       "integrity": "sha512-INb7TM37/mAcsGmc9hyyI6+QR3rR1zVRu36B0NeGXKnOOLiZOfER5SA+N7X7k3yUYRzLWafduTDvJAfDswwEww==",
1509 |       "license": "MIT",
1510 |       "dependencies": {
1511 |         "atomic-sleep": "^1.0.0"
1512 |       }
1513 |     },
1514 |     "node_modules/split2": {
1515 |       "version": "4.2.0",
1516 |       "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz",
1517 |       "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==",
1518 |       "license": "ISC",
1519 |       "engines": {
1520 |         "node": ">= 10.x"
1521 |       }
1522 |     },
1523 |     "node_modules/strip-eof": {
1524 |       "version": "1.0.0",
1525 |       "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz",
1526 |       "integrity": "sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q==",
1527 |       "dev": true,
1528 |       "license": "MIT",
1529 |       "engines": {
1530 |         "node": ">=0.10.0"
1531 |       }
1532 |     },
1533 |     "node_modules/supports-preserve-symlinks-flag": {
1534 |       "version": "1.0.0",
1535 |       "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
1536 |       "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
1537 |       "dev": true,
1538 |       "license": "MIT",
1539 |       "engines": {
1540 |         "node": ">= 0.4"
1541 |       },
1542 |       "funding": {
1543 |         "url": "https://github.com/sponsors/ljharb"
1544 |       }
1545 |     },
1546 |     "node_modules/thread-stream": {
1547 |       "version": "3.1.0",
1548 |       "resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-3.1.0.tgz",
1549 |       "integrity": "sha512-OqyPZ9u96VohAyMfJykzmivOrY2wfMSf3C5TtFJVgN+Hm6aj+voFhlK+kZEIv2FBh1X6Xp3DlnCOfEQ3B2J86A==",
1550 |       "license": "MIT",
1551 |       "dependencies": {
1552 |         "real-require": "^0.2.0"
1553 |       }
1554 |     },
1555 |     "node_modules/tiktoken": {
1556 |       "version": "1.0.21",
1557 |       "resolved": "https://registry.npmjs.org/tiktoken/-/tiktoken-1.0.21.tgz",
1558 |       "integrity": "sha512-/kqtlepLMptX0OgbYD9aMYbM7EFrMZCL7EoHM8Psmg2FuhXoo/bH64KqOiZGGwa6oS9TPdSEDKBnV2LuB8+5vQ==",
1559 |       "license": "MIT"
1560 |     },
1561 |     "node_modules/to-regex-range": {
1562 |       "version": "5.0.1",
1563 |       "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
1564 |       "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
1565 |       "dev": true,
1566 |       "license": "MIT",
1567 |       "dependencies": {
1568 |         "is-number": "^7.0.0"
1569 |       },
1570 |       "engines": {
1571 |         "node": ">=8.0"
1572 |       }
1573 |     },
1574 |     "node_modules/toad-cache": {
1575 |       "version": "3.7.0",
1576 |       "resolved": "https://registry.npmjs.org/toad-cache/-/toad-cache-3.7.0.tgz",
1577 |       "integrity": "sha512-/m8M+2BJUpoJdgAHoG+baCwBT+tf2VraSfkBgl0Y00qIWt41DJ8R5B8nsEw0I58YwF5IZH6z24/2TobDKnqSWw==",
1578 |       "license": "MIT",
1579 |       "engines": {
1580 |         "node": ">=12"
1581 |       }
1582 |     },
1583 |     "node_modules/tr46": {
1584 |       "version": "0.0.3",
1585 |       "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
1586 |       "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==",
1587 |       "license": "MIT"
1588 |     },
1589 |     "node_modules/typescript": {
1590 |       "version": "5.8.3",
1591 |       "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz",
1592 |       "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==",
1593 |       "dev": true,
1594 |       "license": "Apache-2.0",
1595 |       "bin": {
1596 |         "tsc": "bin/tsc",
1597 |         "tsserver": "bin/tsserver"
1598 |       },
1599 |       "engines": {
1600 |         "node": ">=14.17"
1601 |       }
1602 |     },
1603 |     "node_modules/undici": {
1604 |       "version": "7.11.0",
1605 |       "resolved": "https://registry.npmjs.org/undici/-/undici-7.11.0.tgz",
1606 |       "integrity": "sha512-heTSIac3iLhsmZhUCjyS3JQEkZELateufzZuBaVM5RHXdSBMb1LPMQf5x+FH7qjsZYDP0ttAc3nnVpUB+wYbOg==",
1607 |       "license": "MIT",
1608 |       "engines": {
1609 |         "node": ">=20.18.1"
1610 |       }
1611 |     },
1612 |     "node_modules/uuid": {
1613 |       "version": "11.1.0",
1614 |       "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz",
1615 |       "integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==",
1616 |       "funding": [
1617 |         "https://github.com/sponsors/broofa",
1618 |         "https://github.com/sponsors/ctavan"
1619 |       ],
1620 |       "license": "MIT",
1621 |       "bin": {
1622 |         "uuid": "dist/esm/bin/uuid"
1623 |       }
1624 |     },
1625 |     "node_modules/webidl-conversions": {
1626 |       "version": "3.0.1",
1627 |       "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
1628 |       "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==",
1629 |       "license": "BSD-2-Clause"
1630 |     },
1631 |     "node_modules/whatwg-url": {
1632 |       "version": "5.0.0",
1633 |       "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
1634 |       "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
1635 |       "license": "MIT",
1636 |       "dependencies": {
1637 |         "tr46": "~0.0.3",
1638 |         "webidl-conversions": "^3.0.0"
1639 |       }
1640 |     },
1641 |     "node_modules/which": {
1642 |       "version": "1.3.1",
1643 |       "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
1644 |       "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
1645 |       "dev": true,
1646 |       "license": "ISC",
1647 |       "dependencies": {
1648 |         "isexe": "^2.0.0"
1649 |       },
1650 |       "bin": {
1651 |         "which": "bin/which"
1652 |       }
1653 |     },
1654 |     "node_modules/wrappy": {
1655 |       "version": "1.0.2",
1656 |       "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
1657 |       "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
1658 |       "dev": true,
1659 |       "license": "ISC"
1660 |     },
1661 |     "node_modules/ws": {
1662 |       "version": "8.18.3",
1663 |       "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz",
1664 |       "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==",
1665 |       "license": "MIT",
1666 |       "engines": {
1667 |         "node": ">=10.0.0"
1668 |       },
1669 |       "peerDependencies": {
1670 |         "bufferutil": "^4.0.1",
1671 |         "utf-8-validate": ">=5.0.2"
1672 |       },
1673 |       "peerDependenciesMeta": {
1674 |         "bufferutil": {
1675 |           "optional": true
1676 |         },
1677 |         "utf-8-validate": {
1678 |           "optional": true
1679 |         }
1680 |       }
1681 |     }
1682 |   }
1683 | }
1684 | 


--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
 1 | {
 2 |   "name": "@musistudio/claude-code-router",
 3 |   "version": "1.0.18",
 4 |   "description": "Use Claude Code without an Anthropics account and route it to another LLM provider",
 5 |   "bin": {
 6 |     "ccr": "./dist/cli.js"
 7 |   },
 8 |   "scripts": {
 9 |     "build": "esbuild src/cli.ts --bundle --platform=node --outfile=dist/cli.js && shx cp node_modules/tiktoken/tiktoken_bg.wasm dist/tiktoken_bg.wasm"
10 |   },
11 |   "keywords": [
12 |     "claude",
13 |     "code",
14 |     "router",
15 |     "llm",
16 |     "anthropic"
17 |   ],
18 |   "author": "musistudio",
19 |   "license": "MIT",
20 |   "dependencies": {
21 |     "@musistudio/llms": "^1.0.5",
22 |     "dotenv": "^16.4.7",
23 |     "tiktoken": "^1.0.21",
24 |     "uuid": "^11.1.0"
25 |   },
26 |   "devDependencies": {
27 |     "esbuild": "^0.25.1",
28 |     "shx": "^0.4.0",
29 |     "typescript": "^5.8.2"
30 |   },
31 |   "publishConfig": {
32 |     "ignore": [
33 |       "!build/",
34 |       "src/",
35 |       "screenshots/"
36 |     ]
37 |   }
38 | }
39 | 


--------------------------------------------------------------------------------
/pnpm-lock.yaml:
--------------------------------------------------------------------------------
   1 | lockfileVersion: '9.0'
   2 | 
   3 | settings:
   4 |   autoInstallPeers: true
   5 |   excludeLinksFromLockfile: false
   6 | 
   7 | importers:
   8 | 
   9 |   .:
  10 |     dependencies:
  11 |       '@musistudio/llms':
  12 |         specifier: ^1.0.5
  13 |         version: 1.0.5(ws@8.18.3)(zod@3.25.67)
  14 |       dotenv:
  15 |         specifier: ^16.4.7
  16 |         version: 16.6.1
  17 |       tiktoken:
  18 |         specifier: ^1.0.21
  19 |         version: 1.0.21
  20 |       uuid:
  21 |         specifier: ^11.1.0
  22 |         version: 11.1.0
  23 |     devDependencies:
  24 |       esbuild:
  25 |         specifier: ^0.25.1
  26 |         version: 0.25.5
  27 |       shx:
  28 |         specifier: ^0.4.0
  29 |         version: 0.4.0
  30 |       typescript:
  31 |         specifier: ^5.8.2
  32 |         version: 5.8.3
  33 | 
  34 | packages:
  35 | 
  36 |   '@anthropic-ai/sdk@0.54.0':
  37 |     resolution: {integrity: sha512-xyoCtHJnt/qg5GG6IgK+UJEndz8h8ljzt/caKXmq3LfBF81nC/BW6E4x2rOWCZcvsLyVW+e8U5mtIr6UCE/kJw==}
  38 |     hasBin: true
  39 | 
  40 |   '@esbuild/aix-ppc64@0.25.5':
  41 |     resolution: {integrity: sha512-9o3TMmpmftaCMepOdA5k/yDw8SfInyzWWTjYTFCX3kPSDJMROQTb8jg+h9Cnwnmm1vOzvxN7gIfB5V2ewpjtGA==}
  42 |     engines: {node: '>=18'}
  43 |     cpu: [ppc64]
  44 |     os: [aix]
  45 | 
  46 |   '@esbuild/android-arm64@0.25.5':
  47 |     resolution: {integrity: sha512-VGzGhj4lJO+TVGV1v8ntCZWJktV7SGCs3Pn1GRWI1SBFtRALoomm8k5E9Pmwg3HOAal2VDc2F9+PM/rEY6oIDg==}
  48 |     engines: {node: '>=18'}
  49 |     cpu: [arm64]
  50 |     os: [android]
  51 | 
  52 |   '@esbuild/android-arm@0.25.5':
  53 |     resolution: {integrity: sha512-AdJKSPeEHgi7/ZhuIPtcQKr5RQdo6OO2IL87JkianiMYMPbCtot9fxPbrMiBADOWWm3T2si9stAiVsGbTQFkbA==}
  54 |     engines: {node: '>=18'}
  55 |     cpu: [arm]
  56 |     os: [android]
  57 | 
  58 |   '@esbuild/android-x64@0.25.5':
  59 |     resolution: {integrity: sha512-D2GyJT1kjvO//drbRT3Hib9XPwQeWd9vZoBJn+bu/lVsOZ13cqNdDeqIF/xQ5/VmWvMduP6AmXvylO/PIc2isw==}
  60 |     engines: {node: '>=18'}
  61 |     cpu: [x64]
  62 |     os: [android]
  63 | 
  64 |   '@esbuild/darwin-arm64@0.25.5':
  65 |     resolution: {integrity: sha512-GtaBgammVvdF7aPIgH2jxMDdivezgFu6iKpmT+48+F8Hhg5J/sfnDieg0aeG/jfSvkYQU2/pceFPDKlqZzwnfQ==}
  66 |     engines: {node: '>=18'}
  67 |     cpu: [arm64]
  68 |     os: [darwin]
  69 | 
  70 |   '@esbuild/darwin-x64@0.25.5':
  71 |     resolution: {integrity: sha512-1iT4FVL0dJ76/q1wd7XDsXrSW+oLoquptvh4CLR4kITDtqi2e/xwXwdCVH8hVHU43wgJdsq7Gxuzcs6Iq/7bxQ==}
  72 |     engines: {node: '>=18'}
  73 |     cpu: [x64]
  74 |     os: [darwin]
  75 | 
  76 |   '@esbuild/freebsd-arm64@0.25.5':
  77 |     resolution: {integrity: sha512-nk4tGP3JThz4La38Uy/gzyXtpkPW8zSAmoUhK9xKKXdBCzKODMc2adkB2+8om9BDYugz+uGV7sLmpTYzvmz6Sw==}
  78 |     engines: {node: '>=18'}
  79 |     cpu: [arm64]
  80 |     os: [freebsd]
  81 | 
  82 |   '@esbuild/freebsd-x64@0.25.5':
  83 |     resolution: {integrity: sha512-PrikaNjiXdR2laW6OIjlbeuCPrPaAl0IwPIaRv+SMV8CiM8i2LqVUHFC1+8eORgWyY7yhQY+2U2fA55mBzReaw==}
  84 |     engines: {node: '>=18'}
  85 |     cpu: [x64]
  86 |     os: [freebsd]
  87 | 
  88 |   '@esbuild/linux-arm64@0.25.5':
  89 |     resolution: {integrity: sha512-Z9kfb1v6ZlGbWj8EJk9T6czVEjjq2ntSYLY2cw6pAZl4oKtfgQuS4HOq41M/BcoLPzrUbNd+R4BXFyH//nHxVg==}
  90 |     engines: {node: '>=18'}
  91 |     cpu: [arm64]
  92 |     os: [linux]
  93 | 
  94 |   '@esbuild/linux-arm@0.25.5':
  95 |     resolution: {integrity: sha512-cPzojwW2okgh7ZlRpcBEtsX7WBuqbLrNXqLU89GxWbNt6uIg78ET82qifUy3W6OVww6ZWobWub5oqZOVtwolfw==}
  96 |     engines: {node: '>=18'}
  97 |     cpu: [arm]
  98 |     os: [linux]
  99 | 
 100 |   '@esbuild/linux-ia32@0.25.5':
 101 |     resolution: {integrity: sha512-sQ7l00M8bSv36GLV95BVAdhJ2QsIbCuCjh/uYrWiMQSUuV+LpXwIqhgJDcvMTj+VsQmqAHL2yYaasENvJ7CDKA==}
 102 |     engines: {node: '>=18'}
 103 |     cpu: [ia32]
 104 |     os: [linux]
 105 | 
 106 |   '@esbuild/linux-loong64@0.25.5':
 107 |     resolution: {integrity: sha512-0ur7ae16hDUC4OL5iEnDb0tZHDxYmuQyhKhsPBV8f99f6Z9KQM02g33f93rNH5A30agMS46u2HP6qTdEt6Q1kg==}
 108 |     engines: {node: '>=18'}
 109 |     cpu: [loong64]
 110 |     os: [linux]
 111 | 
 112 |   '@esbuild/linux-mips64el@0.25.5':
 113 |     resolution: {integrity: sha512-kB/66P1OsHO5zLz0i6X0RxlQ+3cu0mkxS3TKFvkb5lin6uwZ/ttOkP3Z8lfR9mJOBk14ZwZ9182SIIWFGNmqmg==}
 114 |     engines: {node: '>=18'}
 115 |     cpu: [mips64el]
 116 |     os: [linux]
 117 | 
 118 |   '@esbuild/linux-ppc64@0.25.5':
 119 |     resolution: {integrity: sha512-UZCmJ7r9X2fe2D6jBmkLBMQetXPXIsZjQJCjgwpVDz+YMcS6oFR27alkgGv3Oqkv07bxdvw7fyB71/olceJhkQ==}
 120 |     engines: {node: '>=18'}
 121 |     cpu: [ppc64]
 122 |     os: [linux]
 123 | 
 124 |   '@esbuild/linux-riscv64@0.25.5':
 125 |     resolution: {integrity: sha512-kTxwu4mLyeOlsVIFPfQo+fQJAV9mh24xL+y+Bm6ej067sYANjyEw1dNHmvoqxJUCMnkBdKpvOn0Ahql6+4VyeA==}
 126 |     engines: {node: '>=18'}
 127 |     cpu: [riscv64]
 128 |     os: [linux]
 129 | 
 130 |   '@esbuild/linux-s390x@0.25.5':
 131 |     resolution: {integrity: sha512-K2dSKTKfmdh78uJ3NcWFiqyRrimfdinS5ErLSn3vluHNeHVnBAFWC8a4X5N+7FgVE1EjXS1QDZbpqZBjfrqMTQ==}
 132 |     engines: {node: '>=18'}
 133 |     cpu: [s390x]
 134 |     os: [linux]
 135 | 
 136 |   '@esbuild/linux-x64@0.25.5':
 137 |     resolution: {integrity: sha512-uhj8N2obKTE6pSZ+aMUbqq+1nXxNjZIIjCjGLfsWvVpy7gKCOL6rsY1MhRh9zLtUtAI7vpgLMK6DxjO8Qm9lJw==}
 138 |     engines: {node: '>=18'}
 139 |     cpu: [x64]
 140 |     os: [linux]
 141 | 
 142 |   '@esbuild/netbsd-arm64@0.25.5':
 143 |     resolution: {integrity: sha512-pwHtMP9viAy1oHPvgxtOv+OkduK5ugofNTVDilIzBLpoWAM16r7b/mxBvfpuQDpRQFMfuVr5aLcn4yveGvBZvw==}
 144 |     engines: {node: '>=18'}
 145 |     cpu: [arm64]
 146 |     os: [netbsd]
 147 | 
 148 |   '@esbuild/netbsd-x64@0.25.5':
 149 |     resolution: {integrity: sha512-WOb5fKrvVTRMfWFNCroYWWklbnXH0Q5rZppjq0vQIdlsQKuw6mdSihwSo4RV/YdQ5UCKKvBy7/0ZZYLBZKIbwQ==}
 150 |     engines: {node: '>=18'}
 151 |     cpu: [x64]
 152 |     os: [netbsd]
 153 | 
 154 |   '@esbuild/openbsd-arm64@0.25.5':
 155 |     resolution: {integrity: sha512-7A208+uQKgTxHd0G0uqZO8UjK2R0DDb4fDmERtARjSHWxqMTye4Erz4zZafx7Di9Cv+lNHYuncAkiGFySoD+Mw==}
 156 |     engines: {node: '>=18'}
 157 |     cpu: [arm64]
 158 |     os: [openbsd]
 159 | 
 160 |   '@esbuild/openbsd-x64@0.25.5':
 161 |     resolution: {integrity: sha512-G4hE405ErTWraiZ8UiSoesH8DaCsMm0Cay4fsFWOOUcz8b8rC6uCvnagr+gnioEjWn0wC+o1/TAHt+It+MpIMg==}
 162 |     engines: {node: '>=18'}
 163 |     cpu: [x64]
 164 |     os: [openbsd]
 165 | 
 166 |   '@esbuild/sunos-x64@0.25.5':
 167 |     resolution: {integrity: sha512-l+azKShMy7FxzY0Rj4RCt5VD/q8mG/e+mDivgspo+yL8zW7qEwctQ6YqKX34DTEleFAvCIUviCFX1SDZRSyMQA==}
 168 |     engines: {node: '>=18'}
 169 |     cpu: [x64]
 170 |     os: [sunos]
 171 | 
 172 |   '@esbuild/win32-arm64@0.25.5':
 173 |     resolution: {integrity: sha512-O2S7SNZzdcFG7eFKgvwUEZ2VG9D/sn/eIiz8XRZ1Q/DO5a3s76Xv0mdBzVM5j5R639lXQmPmSo0iRpHqUUrsxw==}
 174 |     engines: {node: '>=18'}
 175 |     cpu: [arm64]
 176 |     os: [win32]
 177 | 
 178 |   '@esbuild/win32-ia32@0.25.5':
 179 |     resolution: {integrity: sha512-onOJ02pqs9h1iMJ1PQphR+VZv8qBMQ77Klcsqv9CNW2w6yLqoURLcgERAIurY6QE63bbLuqgP9ATqajFLK5AMQ==}
 180 |     engines: {node: '>=18'}
 181 |     cpu: [ia32]
 182 |     os: [win32]
 183 | 
 184 |   '@esbuild/win32-x64@0.25.5':
 185 |     resolution: {integrity: sha512-TXv6YnJ8ZMVdX+SXWVBo/0p8LTcrUYngpWjvm91TMjjBQii7Oz11Lw5lbDV5Y0TzuhSJHwiH4hEtC1I42mMS0g==}
 186 |     engines: {node: '>=18'}
 187 |     cpu: [x64]
 188 |     os: [win32]
 189 | 
 190 |   '@fastify/ajv-compiler@4.0.2':
 191 |     resolution: {integrity: sha512-Rkiu/8wIjpsf46Rr+Fitd3HRP+VsxUFDDeag0hs9L0ksfnwx2g7SPQQTFL0E8Qv+rfXzQOxBJnjUB9ITUDjfWQ==}
 192 | 
 193 |   '@fastify/cors@11.0.1':
 194 |     resolution: {integrity: sha512-dmZaE7M1f4SM8ZZuk5RhSsDJ+ezTgI7v3HHRj8Ow9CneczsPLZV6+2j2uwdaSLn8zhTv6QV0F4ZRcqdalGx1pQ==}
 195 | 
 196 |   '@fastify/error@4.2.0':
 197 |     resolution: {integrity: sha512-RSo3sVDXfHskiBZKBPRgnQTtIqpi/7zhJOEmAxCiBcM7d0uwdGdxLlsCaLzGs8v8NnxIRlfG0N51p5yFaOentQ==}
 198 | 
 199 |   '@fastify/fast-json-stringify-compiler@5.0.3':
 200 |     resolution: {integrity: sha512-uik7yYHkLr6fxd8hJSZ8c+xF4WafPK+XzneQDPU+D10r5X19GW8lJcom2YijX2+qtFF1ENJlHXKFM9ouXNJYgQ==}
 201 | 
 202 |   '@fastify/forwarded@3.0.0':
 203 |     resolution: {integrity: sha512-kJExsp4JCms7ipzg7SJ3y8DwmePaELHxKYtg+tZow+k0znUTf3cb+npgyqm8+ATZOdmfgfydIebPDWM172wfyA==}
 204 | 
 205 |   '@fastify/merge-json-schemas@0.2.1':
 206 |     resolution: {integrity: sha512-OA3KGBCy6KtIvLf8DINC5880o5iBlDX4SxzLQS8HorJAbqluzLRn80UXU0bxZn7UOFhFgpRJDasfwn9nG4FG4A==}
 207 | 
 208 |   '@fastify/proxy-addr@5.0.0':
 209 |     resolution: {integrity: sha512-37qVVA1qZ5sgH7KpHkkC4z9SK6StIsIcOmpjvMPXNb3vx2GQxhZocogVYbr2PbbeLCQxYIPDok307xEvRZOzGA==}
 210 | 
 211 |   '@google/genai@1.8.0':
 212 |     resolution: {integrity: sha512-n3KiMFesQCy2R9iSdBIuJ0JWYQ1HZBJJkmt4PPZMGZKvlgHhBAGw1kUMyX+vsAIzprN3lK45DI755lm70wPOOg==}
 213 |     engines: {node: '>=20.0.0'}
 214 |     peerDependencies:
 215 |       '@modelcontextprotocol/sdk': ^1.11.0
 216 |     peerDependenciesMeta:
 217 |       '@modelcontextprotocol/sdk':
 218 |         optional: true
 219 | 
 220 |   '@musistudio/llms@1.0.5':
 221 |     resolution: {integrity: sha512-aa+qSQkr9GMfOZJihTZqPWfCR5ydBN7+yo+32LybgbBI0yhDXWanCpID8vkTBRAs6hpYtqkmSAV0C+RAhXOlFg==}
 222 | 
 223 |   '@nodelib/fs.scandir@2.1.5':
 224 |     resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
 225 |     engines: {node: '>= 8'}
 226 | 
 227 |   '@nodelib/fs.stat@2.0.5':
 228 |     resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==}
 229 |     engines: {node: '>= 8'}
 230 | 
 231 |   '@nodelib/fs.walk@1.2.8':
 232 |     resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==}
 233 |     engines: {node: '>= 8'}
 234 | 
 235 |   abstract-logging@2.0.1:
 236 |     resolution: {integrity: sha512-2BjRTZxTPvheOvGbBslFSYOUkr+SjPtOnrLP33f+VIWLzezQpZcqVg7ja3L4dBXmzzgwT+a029jRx5PCi3JuiA==}
 237 | 
 238 |   agent-base@7.1.3:
 239 |     resolution: {integrity: sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==}
 240 |     engines: {node: '>= 14'}
 241 | 
 242 |   ajv-formats@3.0.1:
 243 |     resolution: {integrity: sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==}
 244 |     peerDependencies:
 245 |       ajv: ^8.0.0
 246 |     peerDependenciesMeta:
 247 |       ajv:
 248 |         optional: true
 249 | 
 250 |   ajv@8.17.1:
 251 |     resolution: {integrity: sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==}
 252 | 
 253 |   atomic-sleep@1.0.0:
 254 |     resolution: {integrity: sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==}
 255 |     engines: {node: '>=8.0.0'}
 256 | 
 257 |   avvio@9.1.0:
 258 |     resolution: {integrity: sha512-fYASnYi600CsH/j9EQov7lECAniYiBFiiAtBNuZYLA2leLe9qOvZzqYHFjtIj6gD2VMoMLP14834LFWvr4IfDw==}
 259 | 
 260 |   base64-js@1.5.1:
 261 |     resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
 262 | 
 263 |   bignumber.js@9.3.0:
 264 |     resolution: {integrity: sha512-EM7aMFTXbptt/wZdMlBv2t8IViwQL+h6SLHosp8Yf0dqJMTnY6iL32opnAB6kAdL0SZPuvcAzFr31o0c/R3/RA==}
 265 | 
 266 |   braces@3.0.3:
 267 |     resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==}
 268 |     engines: {node: '>=8'}
 269 | 
 270 |   buffer-equal-constant-time@1.0.1:
 271 |     resolution: {integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==}
 272 | 
 273 |   cookie@1.0.2:
 274 |     resolution: {integrity: sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==}
 275 |     engines: {node: '>=18'}
 276 | 
 277 |   cross-spawn@6.0.6:
 278 |     resolution: {integrity: sha512-VqCUuhcd1iB+dsv8gxPttb5iZh/D0iubSP21g36KXdEuf6I5JiioesUVjpCdHV9MZRUfVFlvwtIUyPfxo5trtw==}
 279 |     engines: {node: '>=4.8'}
 280 | 
 281 |   debug@4.4.1:
 282 |     resolution: {integrity: sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==}
 283 |     engines: {node: '>=6.0'}
 284 |     peerDependencies:
 285 |       supports-color: '*'
 286 |     peerDependenciesMeta:
 287 |       supports-color:
 288 |         optional: true
 289 | 
 290 |   dequal@2.0.3:
 291 |     resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==}
 292 |     engines: {node: '>=6'}
 293 | 
 294 |   dotenv@16.6.1:
 295 |     resolution: {integrity: sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==}
 296 |     engines: {node: '>=12'}
 297 | 
 298 |   ecdsa-sig-formatter@1.0.11:
 299 |     resolution: {integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==}
 300 | 
 301 |   end-of-stream@1.4.5:
 302 |     resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==}
 303 | 
 304 |   esbuild@0.25.5:
 305 |     resolution: {integrity: sha512-P8OtKZRv/5J5hhz0cUAdu/cLuPIKXpQl1R9pZtvmHWQvrAUVd0UNIPT4IB4W3rNOqVO0rlqHmCIbSwxh/c9yUQ==}
 306 |     engines: {node: '>=18'}
 307 |     hasBin: true
 308 | 
 309 |   execa@1.0.0:
 310 |     resolution: {integrity: sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==}
 311 |     engines: {node: '>=6'}
 312 | 
 313 |   extend@3.0.2:
 314 |     resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==}
 315 | 
 316 |   fast-decode-uri-component@1.0.1:
 317 |     resolution: {integrity: sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg==}
 318 | 
 319 |   fast-deep-equal@3.1.3:
 320 |     resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
 321 | 
 322 |   fast-glob@3.3.3:
 323 |     resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==}
 324 |     engines: {node: '>=8.6.0'}
 325 | 
 326 |   fast-json-stringify@6.0.1:
 327 |     resolution: {integrity: sha512-s7SJE83QKBZwg54dIbD5rCtzOBVD43V1ReWXXYqBgwCwHLYAAT0RQc/FmrQglXqWPpz6omtryJQOau5jI4Nrvg==}
 328 | 
 329 |   fast-querystring@1.1.2:
 330 |     resolution: {integrity: sha512-g6KuKWmFXc0fID8WWH0jit4g0AGBoJhCkJMb1RmbsSEUNvQ+ZC8D6CUZ+GtF8nMzSPXnhiePyyqqipzNNEnHjg==}
 331 | 
 332 |   fast-redact@3.5.0:
 333 |     resolution: {integrity: sha512-dwsoQlS7h9hMeYUq1W++23NDcBLV4KqONnITDV9DjfS3q1SgDGVrBdvvTLUotWtPSD7asWDV9/CmsZPy8Hf70A==}
 334 |     engines: {node: '>=6'}
 335 | 
 336 |   fast-uri@3.0.6:
 337 |     resolution: {integrity: sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==}
 338 | 
 339 |   fastify-plugin@5.0.1:
 340 |     resolution: {integrity: sha512-HCxs+YnRaWzCl+cWRYFnHmeRFyR5GVnJTAaCJQiYzQSDwK9MgJdyAsuL3nh0EWRCYMgQ5MeziymvmAhUHYHDUQ==}
 341 | 
 342 |   fastify@5.4.0:
 343 |     resolution: {integrity: sha512-I4dVlUe+WNQAhKSyv15w+dwUh2EPiEl4X2lGYMmNSgF83WzTMAPKGdWEv5tPsCQOb+SOZwz8Vlta2vF+OeDgRw==}
 344 | 
 345 |   fastq@1.19.1:
 346 |     resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==}
 347 | 
 348 |   fill-range@7.1.1:
 349 |     resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==}
 350 |     engines: {node: '>=8'}
 351 | 
 352 |   find-my-way@9.3.0:
 353 |     resolution: {integrity: sha512-eRoFWQw+Yv2tuYlK2pjFS2jGXSxSppAs3hSQjfxVKxM5amECzIgYYc1FEI8ZmhSh/Ig+FrKEz43NLRKJjYCZVg==}
 354 |     engines: {node: '>=20'}
 355 | 
 356 |   function-bind@1.1.2:
 357 |     resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==}
 358 | 
 359 |   gaxios@6.7.1:
 360 |     resolution: {integrity: sha512-LDODD4TMYx7XXdpwxAVRAIAuB0bzv0s+ywFonY46k126qzQHT9ygyoa9tncmOiQmmDrik65UYsEkv3lbfqQ3yQ==}
 361 |     engines: {node: '>=14'}
 362 | 
 363 |   gcp-metadata@6.1.1:
 364 |     resolution: {integrity: sha512-a4tiq7E0/5fTjxPAaH4jpjkSv/uCaU2p5KC6HVGrvl0cDjA8iBZv4vv1gyzlmK0ZUKqwpOyQMKzZQe3lTit77A==}
 365 |     engines: {node: '>=14'}
 366 | 
 367 |   get-stream@4.1.0:
 368 |     resolution: {integrity: sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==}
 369 |     engines: {node: '>=6'}
 370 | 
 371 |   glob-parent@5.1.2:
 372 |     resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==}
 373 |     engines: {node: '>= 6'}
 374 | 
 375 |   google-auth-library@9.15.1:
 376 |     resolution: {integrity: sha512-Jb6Z0+nvECVz+2lzSMt9u98UsoakXxA2HGHMCxh+so3n90XgYWkq5dur19JAJV7ONiJY22yBTyJB1TSkvPq9Ng==}
 377 |     engines: {node: '>=14'}
 378 | 
 379 |   google-logging-utils@0.0.2:
 380 |     resolution: {integrity: sha512-NEgUnEcBiP5HrPzufUkBzJOD/Sxsco3rLNo1F1TNf7ieU8ryUzBhqba8r756CjLX7rn3fHl6iLEwPYuqpoKgQQ==}
 381 |     engines: {node: '>=14'}
 382 | 
 383 |   gtoken@7.1.0:
 384 |     resolution: {integrity: sha512-pCcEwRi+TKpMlxAQObHDQ56KawURgyAf6jtIY046fJ5tIv3zDe/LEIubckAO8fj6JnAxLdmWkUfNyulQ2iKdEw==}
 385 |     engines: {node: '>=14.0.0'}
 386 | 
 387 |   hasown@2.0.2:
 388 |     resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
 389 |     engines: {node: '>= 0.4'}
 390 | 
 391 |   https-proxy-agent@7.0.6:
 392 |     resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==}
 393 |     engines: {node: '>= 14'}
 394 | 
 395 |   interpret@1.4.0:
 396 |     resolution: {integrity: sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==}
 397 |     engines: {node: '>= 0.10'}
 398 | 
 399 |   ipaddr.js@2.2.0:
 400 |     resolution: {integrity: sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA==}
 401 |     engines: {node: '>= 10'}
 402 | 
 403 |   is-core-module@2.16.1:
 404 |     resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==}
 405 |     engines: {node: '>= 0.4'}
 406 | 
 407 |   is-extglob@2.1.1:
 408 |     resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
 409 |     engines: {node: '>=0.10.0'}
 410 | 
 411 |   is-glob@4.0.3:
 412 |     resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
 413 |     engines: {node: '>=0.10.0'}
 414 | 
 415 |   is-number@7.0.0:
 416 |     resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==}
 417 |     engines: {node: '>=0.12.0'}
 418 | 
 419 |   is-stream@1.1.0:
 420 |     resolution: {integrity: sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==}
 421 |     engines: {node: '>=0.10.0'}
 422 | 
 423 |   is-stream@2.0.1:
 424 |     resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==}
 425 |     engines: {node: '>=8'}
 426 | 
 427 |   isexe@2.0.0:
 428 |     resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
 429 | 
 430 |   json-bigint@1.0.0:
 431 |     resolution: {integrity: sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==}
 432 | 
 433 |   json-schema-ref-resolver@2.0.1:
 434 |     resolution: {integrity: sha512-HG0SIB9X4J8bwbxCbnd5FfPEbcXAJYTi1pBJeP/QPON+w8ovSME8iRG+ElHNxZNX2Qh6eYn1GdzJFS4cDFfx0Q==}
 435 | 
 436 |   json-schema-traverse@1.0.0:
 437 |     resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==}
 438 | 
 439 |   jwa@2.0.1:
 440 |     resolution: {integrity: sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==}
 441 | 
 442 |   jws@4.0.0:
 443 |     resolution: {integrity: sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==}
 444 | 
 445 |   light-my-request@6.6.0:
 446 |     resolution: {integrity: sha512-CHYbu8RtboSIoVsHZ6Ye4cj4Aw/yg2oAFimlF7mNvfDV192LR7nDiKtSIfCuLT7KokPSTn/9kfVLm5OGN0A28A==}
 447 | 
 448 |   merge2@1.4.1:
 449 |     resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
 450 |     engines: {node: '>= 8'}
 451 | 
 452 |   micromatch@4.0.8:
 453 |     resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==}
 454 |     engines: {node: '>=8.6'}
 455 | 
 456 |   minimist@1.2.8:
 457 |     resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==}
 458 | 
 459 |   ms@2.1.3:
 460 |     resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
 461 | 
 462 |   nice-try@1.0.5:
 463 |     resolution: {integrity: sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==}
 464 | 
 465 |   node-fetch@2.7.0:
 466 |     resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==}
 467 |     engines: {node: 4.x || >=6.0.0}
 468 |     peerDependencies:
 469 |       encoding: ^0.1.0
 470 |     peerDependenciesMeta:
 471 |       encoding:
 472 |         optional: true
 473 | 
 474 |   npm-run-path@2.0.2:
 475 |     resolution: {integrity: sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==}
 476 |     engines: {node: '>=4'}
 477 | 
 478 |   on-exit-leak-free@2.1.2:
 479 |     resolution: {integrity: sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==}
 480 |     engines: {node: '>=14.0.0'}
 481 | 
 482 |   once@1.4.0:
 483 |     resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==}
 484 | 
 485 |   openai@5.8.2:
 486 |     resolution: {integrity: sha512-8C+nzoHYgyYOXhHGN6r0fcb4SznuEn1R7YZMvlqDbnCuE0FM2mm3T1HiYW6WIcMS/F1Of2up/cSPjLPaWt0X9Q==}
 487 |     hasBin: true
 488 |     peerDependencies:
 489 |       ws: ^8.18.0
 490 |       zod: ^3.23.8
 491 |     peerDependenciesMeta:
 492 |       ws:
 493 |         optional: true
 494 |       zod:
 495 |         optional: true
 496 | 
 497 |   p-finally@1.0.0:
 498 |     resolution: {integrity: sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==}
 499 |     engines: {node: '>=4'}
 500 | 
 501 |   path-key@2.0.1:
 502 |     resolution: {integrity: sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==}
 503 |     engines: {node: '>=4'}
 504 | 
 505 |   path-parse@1.0.7:
 506 |     resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==}
 507 | 
 508 |   picomatch@2.3.1:
 509 |     resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
 510 |     engines: {node: '>=8.6'}
 511 | 
 512 |   pino-abstract-transport@2.0.0:
 513 |     resolution: {integrity: sha512-F63x5tizV6WCh4R6RHyi2Ml+M70DNRXt/+HANowMflpgGFMAym/VKm6G7ZOQRjqN7XbGxK1Lg9t6ZrtzOaivMw==}
 514 | 
 515 |   pino-std-serializers@7.0.0:
 516 |     resolution: {integrity: sha512-e906FRY0+tV27iq4juKzSYPbUj2do2X2JX4EzSca1631EB2QJQUqGbDuERal7LCtOpxl6x3+nvo9NPZcmjkiFA==}
 517 | 
 518 |   pino@9.7.0:
 519 |     resolution: {integrity: sha512-vnMCM6xZTb1WDmLvtG2lE/2p+t9hDEIvTWJsu6FejkE62vB7gDhvzrpFR4Cw2to+9JNQxVnkAKVPA1KPB98vWg==}
 520 |     hasBin: true
 521 | 
 522 |   process-warning@4.0.1:
 523 |     resolution: {integrity: sha512-3c2LzQ3rY9d0hc1emcsHhfT9Jwz0cChib/QN89oME2R451w5fy3f0afAhERFZAwrbDU43wk12d0ORBpDVME50Q==}
 524 | 
 525 |   process-warning@5.0.0:
 526 |     resolution: {integrity: sha512-a39t9ApHNx2L4+HBnQKqxxHNs1r7KF+Intd8Q/g1bUh6q0WIp9voPXJ/x0j+ZL45KF1pJd9+q2jLIRMfvEshkA==}
 527 | 
 528 |   pump@3.0.3:
 529 |     resolution: {integrity: sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==}
 530 | 
 531 |   queue-microtask@1.2.3:
 532 |     resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
 533 | 
 534 |   quick-format-unescaped@4.0.4:
 535 |     resolution: {integrity: sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==}
 536 | 
 537 |   real-require@0.2.0:
 538 |     resolution: {integrity: sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==}
 539 |     engines: {node: '>= 12.13.0'}
 540 | 
 541 |   rechoir@0.6.2:
 542 |     resolution: {integrity: sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==}
 543 |     engines: {node: '>= 0.10'}
 544 | 
 545 |   require-from-string@2.0.2:
 546 |     resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==}
 547 |     engines: {node: '>=0.10.0'}
 548 | 
 549 |   resolve@1.22.10:
 550 |     resolution: {integrity: sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==}
 551 |     engines: {node: '>= 0.4'}
 552 |     hasBin: true
 553 | 
 554 |   ret@0.5.0:
 555 |     resolution: {integrity: sha512-I1XxrZSQ+oErkRR4jYbAyEEu2I0avBvvMM5JN+6EBprOGRCs63ENqZ3vjavq8fBw2+62G5LF5XelKwuJpcvcxw==}
 556 |     engines: {node: '>=10'}
 557 | 
 558 |   reusify@1.1.0:
 559 |     resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==}
 560 |     engines: {iojs: '>=1.0.0', node: '>=0.10.0'}
 561 | 
 562 |   rfdc@1.4.1:
 563 |     resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==}
 564 | 
 565 |   run-parallel@1.2.0:
 566 |     resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
 567 | 
 568 |   safe-buffer@5.2.1:
 569 |     resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==}
 570 | 
 571 |   safe-regex2@5.0.0:
 572 |     resolution: {integrity: sha512-YwJwe5a51WlK7KbOJREPdjNrpViQBI3p4T50lfwPuDhZnE3XGVTlGvi+aolc5+RvxDD6bnUmjVsU9n1eboLUYw==}
 573 | 
 574 |   safe-stable-stringify@2.5.0:
 575 |     resolution: {integrity: sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==}
 576 |     engines: {node: '>=10'}
 577 | 
 578 |   secure-json-parse@4.0.0:
 579 |     resolution: {integrity: sha512-dxtLJO6sc35jWidmLxo7ij+Eg48PM/kleBsxpC8QJE0qJICe+KawkDQmvCMZUr9u7WKVHgMW6vy3fQ7zMiFZMA==}
 580 | 
 581 |   semver@5.7.2:
 582 |     resolution: {integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==}
 583 |     hasBin: true
 584 | 
 585 |   semver@7.7.2:
 586 |     resolution: {integrity: sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==}
 587 |     engines: {node: '>=10'}
 588 |     hasBin: true
 589 | 
 590 |   set-cookie-parser@2.7.1:
 591 |     resolution: {integrity: sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==}
 592 | 
 593 |   shebang-command@1.2.0:
 594 |     resolution: {integrity: sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==}
 595 |     engines: {node: '>=0.10.0'}
 596 | 
 597 |   shebang-regex@1.0.0:
 598 |     resolution: {integrity: sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==}
 599 |     engines: {node: '>=0.10.0'}
 600 | 
 601 |   shelljs@0.9.2:
 602 |     resolution: {integrity: sha512-S3I64fEiKgTZzKCC46zT/Ib9meqofLrQVbpSswtjFfAVDW+AZ54WTnAM/3/yENoxz/V1Cy6u3kiiEbQ4DNphvw==}
 603 |     engines: {node: '>=18'}
 604 |     hasBin: true
 605 | 
 606 |   shx@0.4.0:
 607 |     resolution: {integrity: sha512-Z0KixSIlGPpijKgcH6oCMCbltPImvaKy0sGH8AkLRXw1KyzpKtaCTizP2xen+hNDqVF4xxgvA0KXSb9o4Q6hnA==}
 608 |     engines: {node: '>=18'}
 609 |     hasBin: true
 610 | 
 611 |   signal-exit@3.0.7:
 612 |     resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==}
 613 | 
 614 |   sonic-boom@4.2.0:
 615 |     resolution: {integrity: sha512-INb7TM37/mAcsGmc9hyyI6+QR3rR1zVRu36B0NeGXKnOOLiZOfER5SA+N7X7k3yUYRzLWafduTDvJAfDswwEww==}
 616 | 
 617 |   split2@4.2.0:
 618 |     resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==}
 619 |     engines: {node: '>= 10.x'}
 620 | 
 621 |   strip-eof@1.0.0:
 622 |     resolution: {integrity: sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q==}
 623 |     engines: {node: '>=0.10.0'}
 624 | 
 625 |   supports-preserve-symlinks-flag@1.0.0:
 626 |     resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
 627 |     engines: {node: '>= 0.4'}
 628 | 
 629 |   thread-stream@3.1.0:
 630 |     resolution: {integrity: sha512-OqyPZ9u96VohAyMfJykzmivOrY2wfMSf3C5TtFJVgN+Hm6aj+voFhlK+kZEIv2FBh1X6Xp3DlnCOfEQ3B2J86A==}
 631 | 
 632 |   tiktoken@1.0.21:
 633 |     resolution: {integrity: sha512-/kqtlepLMptX0OgbYD9aMYbM7EFrMZCL7EoHM8Psmg2FuhXoo/bH64KqOiZGGwa6oS9TPdSEDKBnV2LuB8+5vQ==}
 634 | 
 635 |   to-regex-range@5.0.1:
 636 |     resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
 637 |     engines: {node: '>=8.0'}
 638 | 
 639 |   toad-cache@3.7.0:
 640 |     resolution: {integrity: sha512-/m8M+2BJUpoJdgAHoG+baCwBT+tf2VraSfkBgl0Y00qIWt41DJ8R5B8nsEw0I58YwF5IZH6z24/2TobDKnqSWw==}
 641 |     engines: {node: '>=12'}
 642 | 
 643 |   tr46@0.0.3:
 644 |     resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==}
 645 | 
 646 |   typescript@5.8.3:
 647 |     resolution: {integrity: sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==}
 648 |     engines: {node: '>=14.17'}
 649 |     hasBin: true
 650 | 
 651 |   undici@7.11.0:
 652 |     resolution: {integrity: sha512-heTSIac3iLhsmZhUCjyS3JQEkZELateufzZuBaVM5RHXdSBMb1LPMQf5x+FH7qjsZYDP0ttAc3nnVpUB+wYbOg==}
 653 |     engines: {node: '>=20.18.1'}
 654 | 
 655 |   uuid@11.1.0:
 656 |     resolution: {integrity: sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==}
 657 |     hasBin: true
 658 | 
 659 |   uuid@9.0.1:
 660 |     resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==}
 661 |     hasBin: true
 662 | 
 663 |   webidl-conversions@3.0.1:
 664 |     resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==}
 665 | 
 666 |   whatwg-url@5.0.0:
 667 |     resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==}
 668 | 
 669 |   which@1.3.1:
 670 |     resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==}
 671 |     hasBin: true
 672 | 
 673 |   wrappy@1.0.2:
 674 |     resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
 675 | 
 676 |   ws@8.18.3:
 677 |     resolution: {integrity: sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==}
 678 |     engines: {node: '>=10.0.0'}
 679 |     peerDependencies:
 680 |       bufferutil: ^4.0.1
 681 |       utf-8-validate: '>=5.0.2'
 682 |     peerDependenciesMeta:
 683 |       bufferutil:
 684 |         optional: true
 685 |       utf-8-validate:
 686 |         optional: true
 687 | 
 688 |   zod-to-json-schema@3.24.6:
 689 |     resolution: {integrity: sha512-h/z3PKvcTcTetyjl1fkj79MHNEjm+HpD6NXheWjzOekY7kV+lwDYnHw+ivHkijnCSMz1yJaWBD9vu/Fcmk+vEg==}
 690 |     peerDependencies:
 691 |       zod: ^3.24.1
 692 | 
 693 |   zod@3.25.67:
 694 |     resolution: {integrity: sha512-idA2YXwpCdqUSKRCACDE6ItZD9TZzy3OZMtpfLoh6oPR47lipysRrJfjzMqFxQ3uJuUPyUeWe1r9vLH33xO/Qw==}
 695 | 
 696 | snapshots:
 697 | 
 698 |   '@anthropic-ai/sdk@0.54.0': {}
 699 | 
 700 |   '@esbuild/aix-ppc64@0.25.5':
 701 |     optional: true
 702 | 
 703 |   '@esbuild/android-arm64@0.25.5':
 704 |     optional: true
 705 | 
 706 |   '@esbuild/android-arm@0.25.5':
 707 |     optional: true
 708 | 
 709 |   '@esbuild/android-x64@0.25.5':
 710 |     optional: true
 711 | 
 712 |   '@esbuild/darwin-arm64@0.25.5':
 713 |     optional: true
 714 | 
 715 |   '@esbuild/darwin-x64@0.25.5':
 716 |     optional: true
 717 | 
 718 |   '@esbuild/freebsd-arm64@0.25.5':
 719 |     optional: true
 720 | 
 721 |   '@esbuild/freebsd-x64@0.25.5':
 722 |     optional: true
 723 | 
 724 |   '@esbuild/linux-arm64@0.25.5':
 725 |     optional: true
 726 | 
 727 |   '@esbuild/linux-arm@0.25.5':
 728 |     optional: true
 729 | 
 730 |   '@esbuild/linux-ia32@0.25.5':
 731 |     optional: true
 732 | 
 733 |   '@esbuild/linux-loong64@0.25.5':
 734 |     optional: true
 735 | 
 736 |   '@esbuild/linux-mips64el@0.25.5':
 737 |     optional: true
 738 | 
 739 |   '@esbuild/linux-ppc64@0.25.5':
 740 |     optional: true
 741 | 
 742 |   '@esbuild/linux-riscv64@0.25.5':
 743 |     optional: true
 744 | 
 745 |   '@esbuild/linux-s390x@0.25.5':
 746 |     optional: true
 747 | 
 748 |   '@esbuild/linux-x64@0.25.5':
 749 |     optional: true
 750 | 
 751 |   '@esbuild/netbsd-arm64@0.25.5':
 752 |     optional: true
 753 | 
 754 |   '@esbuild/netbsd-x64@0.25.5':
 755 |     optional: true
 756 | 
 757 |   '@esbuild/openbsd-arm64@0.25.5':
 758 |     optional: true
 759 | 
 760 |   '@esbuild/openbsd-x64@0.25.5':
 761 |     optional: true
 762 | 
 763 |   '@esbuild/sunos-x64@0.25.5':
 764 |     optional: true
 765 | 
 766 |   '@esbuild/win32-arm64@0.25.5':
 767 |     optional: true
 768 | 
 769 |   '@esbuild/win32-ia32@0.25.5':
 770 |     optional: true
 771 | 
 772 |   '@esbuild/win32-x64@0.25.5':
 773 |     optional: true
 774 | 
 775 |   '@fastify/ajv-compiler@4.0.2':
 776 |     dependencies:
 777 |       ajv: 8.17.1
 778 |       ajv-formats: 3.0.1(ajv@8.17.1)
 779 |       fast-uri: 3.0.6
 780 | 
 781 |   '@fastify/cors@11.0.1':
 782 |     dependencies:
 783 |       fastify-plugin: 5.0.1
 784 |       toad-cache: 3.7.0
 785 | 
 786 |   '@fastify/error@4.2.0': {}
 787 | 
 788 |   '@fastify/fast-json-stringify-compiler@5.0.3':
 789 |     dependencies:
 790 |       fast-json-stringify: 6.0.1
 791 | 
 792 |   '@fastify/forwarded@3.0.0': {}
 793 | 
 794 |   '@fastify/merge-json-schemas@0.2.1':
 795 |     dependencies:
 796 |       dequal: 2.0.3
 797 | 
 798 |   '@fastify/proxy-addr@5.0.0':
 799 |     dependencies:
 800 |       '@fastify/forwarded': 3.0.0
 801 |       ipaddr.js: 2.2.0
 802 | 
 803 |   '@google/genai@1.8.0':
 804 |     dependencies:
 805 |       google-auth-library: 9.15.1
 806 |       ws: 8.18.3
 807 |       zod: 3.25.67
 808 |       zod-to-json-schema: 3.24.6(zod@3.25.67)
 809 |     transitivePeerDependencies:
 810 |       - bufferutil
 811 |       - encoding
 812 |       - supports-color
 813 |       - utf-8-validate
 814 | 
 815 |   '@musistudio/llms@1.0.5(ws@8.18.3)(zod@3.25.67)':
 816 |     dependencies:
 817 |       '@anthropic-ai/sdk': 0.54.0
 818 |       '@fastify/cors': 11.0.1
 819 |       '@google/genai': 1.8.0
 820 |       dotenv: 16.6.1
 821 |       fastify: 5.4.0
 822 |       openai: 5.8.2(ws@8.18.3)(zod@3.25.67)
 823 |       undici: 7.11.0
 824 |     transitivePeerDependencies:
 825 |       - '@modelcontextprotocol/sdk'
 826 |       - bufferutil
 827 |       - encoding
 828 |       - supports-color
 829 |       - utf-8-validate
 830 |       - ws
 831 |       - zod
 832 | 
 833 |   '@nodelib/fs.scandir@2.1.5':
 834 |     dependencies:
 835 |       '@nodelib/fs.stat': 2.0.5
 836 |       run-parallel: 1.2.0
 837 | 
 838 |   '@nodelib/fs.stat@2.0.5': {}
 839 | 
 840 |   '@nodelib/fs.walk@1.2.8':
 841 |     dependencies:
 842 |       '@nodelib/fs.scandir': 2.1.5
 843 |       fastq: 1.19.1
 844 | 
 845 |   abstract-logging@2.0.1: {}
 846 | 
 847 |   agent-base@7.1.3: {}
 848 | 
 849 |   ajv-formats@3.0.1(ajv@8.17.1):
 850 |     optionalDependencies:
 851 |       ajv: 8.17.1
 852 | 
 853 |   ajv@8.17.1:
 854 |     dependencies:
 855 |       fast-deep-equal: 3.1.3
 856 |       fast-uri: 3.0.6
 857 |       json-schema-traverse: 1.0.0
 858 |       require-from-string: 2.0.2
 859 | 
 860 |   atomic-sleep@1.0.0: {}
 861 | 
 862 |   avvio@9.1.0:
 863 |     dependencies:
 864 |       '@fastify/error': 4.2.0
 865 |       fastq: 1.19.1
 866 | 
 867 |   base64-js@1.5.1: {}
 868 | 
 869 |   bignumber.js@9.3.0: {}
 870 | 
 871 |   braces@3.0.3:
 872 |     dependencies:
 873 |       fill-range: 7.1.1
 874 | 
 875 |   buffer-equal-constant-time@1.0.1: {}
 876 | 
 877 |   cookie@1.0.2: {}
 878 | 
 879 |   cross-spawn@6.0.6:
 880 |     dependencies:
 881 |       nice-try: 1.0.5
 882 |       path-key: 2.0.1
 883 |       semver: 5.7.2
 884 |       shebang-command: 1.2.0
 885 |       which: 1.3.1
 886 | 
 887 |   debug@4.4.1:
 888 |     dependencies:
 889 |       ms: 2.1.3
 890 | 
 891 |   dequal@2.0.3: {}
 892 | 
 893 |   dotenv@16.6.1: {}
 894 | 
 895 |   ecdsa-sig-formatter@1.0.11:
 896 |     dependencies:
 897 |       safe-buffer: 5.2.1
 898 | 
 899 |   end-of-stream@1.4.5:
 900 |     dependencies:
 901 |       once: 1.4.0
 902 | 
 903 |   esbuild@0.25.5:
 904 |     optionalDependencies:
 905 |       '@esbuild/aix-ppc64': 0.25.5
 906 |       '@esbuild/android-arm': 0.25.5
 907 |       '@esbuild/android-arm64': 0.25.5
 908 |       '@esbuild/android-x64': 0.25.5
 909 |       '@esbuild/darwin-arm64': 0.25.5
 910 |       '@esbuild/darwin-x64': 0.25.5
 911 |       '@esbuild/freebsd-arm64': 0.25.5
 912 |       '@esbuild/freebsd-x64': 0.25.5
 913 |       '@esbuild/linux-arm': 0.25.5
 914 |       '@esbuild/linux-arm64': 0.25.5
 915 |       '@esbuild/linux-ia32': 0.25.5
 916 |       '@esbuild/linux-loong64': 0.25.5
 917 |       '@esbuild/linux-mips64el': 0.25.5
 918 |       '@esbuild/linux-ppc64': 0.25.5
 919 |       '@esbuild/linux-riscv64': 0.25.5
 920 |       '@esbuild/linux-s390x': 0.25.5
 921 |       '@esbuild/linux-x64': 0.25.5
 922 |       '@esbuild/netbsd-arm64': 0.25.5
 923 |       '@esbuild/netbsd-x64': 0.25.5
 924 |       '@esbuild/openbsd-arm64': 0.25.5
 925 |       '@esbuild/openbsd-x64': 0.25.5
 926 |       '@esbuild/sunos-x64': 0.25.5
 927 |       '@esbuild/win32-arm64': 0.25.5
 928 |       '@esbuild/win32-ia32': 0.25.5
 929 |       '@esbuild/win32-x64': 0.25.5
 930 | 
 931 |   execa@1.0.0:
 932 |     dependencies:
 933 |       cross-spawn: 6.0.6
 934 |       get-stream: 4.1.0
 935 |       is-stream: 1.1.0
 936 |       npm-run-path: 2.0.2
 937 |       p-finally: 1.0.0
 938 |       signal-exit: 3.0.7
 939 |       strip-eof: 1.0.0
 940 | 
 941 |   extend@3.0.2: {}
 942 | 
 943 |   fast-decode-uri-component@1.0.1: {}
 944 | 
 945 |   fast-deep-equal@3.1.3: {}
 946 | 
 947 |   fast-glob@3.3.3:
 948 |     dependencies:
 949 |       '@nodelib/fs.stat': 2.0.5
 950 |       '@nodelib/fs.walk': 1.2.8
 951 |       glob-parent: 5.1.2
 952 |       merge2: 1.4.1
 953 |       micromatch: 4.0.8
 954 | 
 955 |   fast-json-stringify@6.0.1:
 956 |     dependencies:
 957 |       '@fastify/merge-json-schemas': 0.2.1
 958 |       ajv: 8.17.1
 959 |       ajv-formats: 3.0.1(ajv@8.17.1)
 960 |       fast-uri: 3.0.6
 961 |       json-schema-ref-resolver: 2.0.1
 962 |       rfdc: 1.4.1
 963 | 
 964 |   fast-querystring@1.1.2:
 965 |     dependencies:
 966 |       fast-decode-uri-component: 1.0.1
 967 | 
 968 |   fast-redact@3.5.0: {}
 969 | 
 970 |   fast-uri@3.0.6: {}
 971 | 
 972 |   fastify-plugin@5.0.1: {}
 973 | 
 974 |   fastify@5.4.0:
 975 |     dependencies:
 976 |       '@fastify/ajv-compiler': 4.0.2
 977 |       '@fastify/error': 4.2.0
 978 |       '@fastify/fast-json-stringify-compiler': 5.0.3
 979 |       '@fastify/proxy-addr': 5.0.0
 980 |       abstract-logging: 2.0.1
 981 |       avvio: 9.1.0
 982 |       fast-json-stringify: 6.0.1
 983 |       find-my-way: 9.3.0
 984 |       light-my-request: 6.6.0
 985 |       pino: 9.7.0
 986 |       process-warning: 5.0.0
 987 |       rfdc: 1.4.1
 988 |       secure-json-parse: 4.0.0
 989 |       semver: 7.7.2
 990 |       toad-cache: 3.7.0
 991 | 
 992 |   fastq@1.19.1:
 993 |     dependencies:
 994 |       reusify: 1.1.0
 995 | 
 996 |   fill-range@7.1.1:
 997 |     dependencies:
 998 |       to-regex-range: 5.0.1
 999 | 
1000 |   find-my-way@9.3.0:
1001 |     dependencies:
1002 |       fast-deep-equal: 3.1.3
1003 |       fast-querystring: 1.1.2
1004 |       safe-regex2: 5.0.0
1005 | 
1006 |   function-bind@1.1.2: {}
1007 | 
1008 |   gaxios@6.7.1:
1009 |     dependencies:
1010 |       extend: 3.0.2
1011 |       https-proxy-agent: 7.0.6
1012 |       is-stream: 2.0.1
1013 |       node-fetch: 2.7.0
1014 |       uuid: 9.0.1
1015 |     transitivePeerDependencies:
1016 |       - encoding
1017 |       - supports-color
1018 | 
1019 |   gcp-metadata@6.1.1:
1020 |     dependencies:
1021 |       gaxios: 6.7.1
1022 |       google-logging-utils: 0.0.2
1023 |       json-bigint: 1.0.0
1024 |     transitivePeerDependencies:
1025 |       - encoding
1026 |       - supports-color
1027 | 
1028 |   get-stream@4.1.0:
1029 |     dependencies:
1030 |       pump: 3.0.3
1031 | 
1032 |   glob-parent@5.1.2:
1033 |     dependencies:
1034 |       is-glob: 4.0.3
1035 | 
1036 |   google-auth-library@9.15.1:
1037 |     dependencies:
1038 |       base64-js: 1.5.1
1039 |       ecdsa-sig-formatter: 1.0.11
1040 |       gaxios: 6.7.1
1041 |       gcp-metadata: 6.1.1
1042 |       gtoken: 7.1.0
1043 |       jws: 4.0.0
1044 |     transitivePeerDependencies:
1045 |       - encoding
1046 |       - supports-color
1047 | 
1048 |   google-logging-utils@0.0.2: {}
1049 | 
1050 |   gtoken@7.1.0:
1051 |     dependencies:
1052 |       gaxios: 6.7.1
1053 |       jws: 4.0.0
1054 |     transitivePeerDependencies:
1055 |       - encoding
1056 |       - supports-color
1057 | 
1058 |   hasown@2.0.2:
1059 |     dependencies:
1060 |       function-bind: 1.1.2
1061 | 
1062 |   https-proxy-agent@7.0.6:
1063 |     dependencies:
1064 |       agent-base: 7.1.3
1065 |       debug: 4.4.1
1066 |     transitivePeerDependencies:
1067 |       - supports-color
1068 | 
1069 |   interpret@1.4.0: {}
1070 | 
1071 |   ipaddr.js@2.2.0: {}
1072 | 
1073 |   is-core-module@2.16.1:
1074 |     dependencies:
1075 |       hasown: 2.0.2
1076 | 
1077 |   is-extglob@2.1.1: {}
1078 | 
1079 |   is-glob@4.0.3:
1080 |     dependencies:
1081 |       is-extglob: 2.1.1
1082 | 
1083 |   is-number@7.0.0: {}
1084 | 
1085 |   is-stream@1.1.0: {}
1086 | 
1087 |   is-stream@2.0.1: {}
1088 | 
1089 |   isexe@2.0.0: {}
1090 | 
1091 |   json-bigint@1.0.0:
1092 |     dependencies:
1093 |       bignumber.js: 9.3.0
1094 | 
1095 |   json-schema-ref-resolver@2.0.1:
1096 |     dependencies:
1097 |       dequal: 2.0.3
1098 | 
1099 |   json-schema-traverse@1.0.0: {}
1100 | 
1101 |   jwa@2.0.1:
1102 |     dependencies:
1103 |       buffer-equal-constant-time: 1.0.1
1104 |       ecdsa-sig-formatter: 1.0.11
1105 |       safe-buffer: 5.2.1
1106 | 
1107 |   jws@4.0.0:
1108 |     dependencies:
1109 |       jwa: 2.0.1
1110 |       safe-buffer: 5.2.1
1111 | 
1112 |   light-my-request@6.6.0:
1113 |     dependencies:
1114 |       cookie: 1.0.2
1115 |       process-warning: 4.0.1
1116 |       set-cookie-parser: 2.7.1
1117 | 
1118 |   merge2@1.4.1: {}
1119 | 
1120 |   micromatch@4.0.8:
1121 |     dependencies:
1122 |       braces: 3.0.3
1123 |       picomatch: 2.3.1
1124 | 
1125 |   minimist@1.2.8: {}
1126 | 
1127 |   ms@2.1.3: {}
1128 | 
1129 |   nice-try@1.0.5: {}
1130 | 
1131 |   node-fetch@2.7.0:
1132 |     dependencies:
1133 |       whatwg-url: 5.0.0
1134 | 
1135 |   npm-run-path@2.0.2:
1136 |     dependencies:
1137 |       path-key: 2.0.1
1138 | 
1139 |   on-exit-leak-free@2.1.2: {}
1140 | 
1141 |   once@1.4.0:
1142 |     dependencies:
1143 |       wrappy: 1.0.2
1144 | 
1145 |   openai@5.8.2(ws@8.18.3)(zod@3.25.67):
1146 |     optionalDependencies:
1147 |       ws: 8.18.3
1148 |       zod: 3.25.67
1149 | 
1150 |   p-finally@1.0.0: {}
1151 | 
1152 |   path-key@2.0.1: {}
1153 | 
1154 |   path-parse@1.0.7: {}
1155 | 
1156 |   picomatch@2.3.1: {}
1157 | 
1158 |   pino-abstract-transport@2.0.0:
1159 |     dependencies:
1160 |       split2: 4.2.0
1161 | 
1162 |   pino-std-serializers@7.0.0: {}
1163 | 
1164 |   pino@9.7.0:
1165 |     dependencies:
1166 |       atomic-sleep: 1.0.0
1167 |       fast-redact: 3.5.0
1168 |       on-exit-leak-free: 2.1.2
1169 |       pino-abstract-transport: 2.0.0
1170 |       pino-std-serializers: 7.0.0
1171 |       process-warning: 5.0.0
1172 |       quick-format-unescaped: 4.0.4
1173 |       real-require: 0.2.0
1174 |       safe-stable-stringify: 2.5.0
1175 |       sonic-boom: 4.2.0
1176 |       thread-stream: 3.1.0
1177 | 
1178 |   process-warning@4.0.1: {}
1179 | 
1180 |   process-warning@5.0.0: {}
1181 | 
1182 |   pump@3.0.3:
1183 |     dependencies:
1184 |       end-of-stream: 1.4.5
1185 |       once: 1.4.0
1186 | 
1187 |   queue-microtask@1.2.3: {}
1188 | 
1189 |   quick-format-unescaped@4.0.4: {}
1190 | 
1191 |   real-require@0.2.0: {}
1192 | 
1193 |   rechoir@0.6.2:
1194 |     dependencies:
1195 |       resolve: 1.22.10
1196 | 
1197 |   require-from-string@2.0.2: {}
1198 | 
1199 |   resolve@1.22.10:
1200 |     dependencies:
1201 |       is-core-module: 2.16.1
1202 |       path-parse: 1.0.7
1203 |       supports-preserve-symlinks-flag: 1.0.0
1204 | 
1205 |   ret@0.5.0: {}
1206 | 
1207 |   reusify@1.1.0: {}
1208 | 
1209 |   rfdc@1.4.1: {}
1210 | 
1211 |   run-parallel@1.2.0:
1212 |     dependencies:
1213 |       queue-microtask: 1.2.3
1214 | 
1215 |   safe-buffer@5.2.1: {}
1216 | 
1217 |   safe-regex2@5.0.0:
1218 |     dependencies:
1219 |       ret: 0.5.0
1220 | 
1221 |   safe-stable-stringify@2.5.0: {}
1222 | 
1223 |   secure-json-parse@4.0.0: {}
1224 | 
1225 |   semver@5.7.2: {}
1226 | 
1227 |   semver@7.7.2: {}
1228 | 
1229 |   set-cookie-parser@2.7.1: {}
1230 | 
1231 |   shebang-command@1.2.0:
1232 |     dependencies:
1233 |       shebang-regex: 1.0.0
1234 | 
1235 |   shebang-regex@1.0.0: {}
1236 | 
1237 |   shelljs@0.9.2:
1238 |     dependencies:
1239 |       execa: 1.0.0
1240 |       fast-glob: 3.3.3
1241 |       interpret: 1.4.0
1242 |       rechoir: 0.6.2
1243 | 
1244 |   shx@0.4.0:
1245 |     dependencies:
1246 |       minimist: 1.2.8
1247 |       shelljs: 0.9.2
1248 | 
1249 |   signal-exit@3.0.7: {}
1250 | 
1251 |   sonic-boom@4.2.0:
1252 |     dependencies:
1253 |       atomic-sleep: 1.0.0
1254 | 
1255 |   split2@4.2.0: {}
1256 | 
1257 |   strip-eof@1.0.0: {}
1258 | 
1259 |   supports-preserve-symlinks-flag@1.0.0: {}
1260 | 
1261 |   thread-stream@3.1.0:
1262 |     dependencies:
1263 |       real-require: 0.2.0
1264 | 
1265 |   tiktoken@1.0.21: {}
1266 | 
1267 |   to-regex-range@5.0.1:
1268 |     dependencies:
1269 |       is-number: 7.0.0
1270 | 
1271 |   toad-cache@3.7.0: {}
1272 | 
1273 |   tr46@0.0.3: {}
1274 | 
1275 |   typescript@5.8.3: {}
1276 | 
1277 |   undici@7.11.0: {}
1278 | 
1279 |   uuid@11.1.0: {}
1280 | 
1281 |   uuid@9.0.1: {}
1282 | 
1283 |   webidl-conversions@3.0.1: {}
1284 | 
1285 |   whatwg-url@5.0.0:
1286 |     dependencies:
1287 |       tr46: 0.0.3
1288 |       webidl-conversions: 3.0.1
1289 | 
1290 |   which@1.3.1:
1291 |     dependencies:
1292 |       isexe: 2.0.0
1293 | 
1294 |   wrappy@1.0.2: {}
1295 | 
1296 |   ws@8.18.3: {}
1297 | 
1298 |   zod-to-json-schema@3.24.6(zod@3.25.67):
1299 |     dependencies:
1300 |       zod: 3.25.67
1301 | 
1302 |   zod@3.25.67: {}
1303 | 


--------------------------------------------------------------------------------
/screenshoots/claude-code.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/musistudio/claude-code-router/5174ddacfcb0cc22cdb274d1266fd978d56537bc/screenshoots/claude-code.png


--------------------------------------------------------------------------------
/screenshoots/contexterror.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/musistudio/claude-code-router/5174ddacfcb0cc22cdb274d1266fd978d56537bc/screenshoots/contexterror.jpg


--------------------------------------------------------------------------------
/screenshoots/demo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/musistudio/claude-code-router/5174ddacfcb0cc22cdb274d1266fd978d56537bc/screenshoots/demo.png


--------------------------------------------------------------------------------
/screenshoots/normal.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/musistudio/claude-code-router/5174ddacfcb0cc22cdb274d1266fd978d56537bc/screenshoots/normal.png


--------------------------------------------------------------------------------
/screenshoots/router.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/musistudio/claude-code-router/5174ddacfcb0cc22cdb274d1266fd978d56537bc/screenshoots/router.png


--------------------------------------------------------------------------------
/src/cli.ts:
--------------------------------------------------------------------------------
  1 | #!/usr/bin/env node
  2 | import { run } from "./index";
  3 | import { showStatus } from "./utils/status";
  4 | import { executeCodeCommand } from "./utils/codeCommand";
  5 | import { cleanupPidFile, isServiceRunning } from "./utils/processCheck";
  6 | import { version } from "../package.json";
  7 | import { spawn } from "child_process";
  8 | import { PID_FILE, REFERENCE_COUNT_FILE } from "./constants";
  9 | import { existsSync, readFileSync } from "fs";
 10 | import {join} from "path";
 11 | 
 12 | const command = process.argv[2];
 13 | 
 14 | const HELP_TEXT = `
 15 | Usage: ccr [command]
 16 | 
 17 | Commands:
 18 |   start         Start service 
 19 |   stop          Stop service
 20 |   status        Show service status
 21 |   code          Execute code command
 22 |   -v, version   Show version information
 23 |   -h, help      Show help information
 24 | 
 25 | Example:
 26 |   ccr start
 27 |   ccr code "Write a Hello World"
 28 | `;
 29 | 
 30 | async function waitForService(
 31 |   timeout = 10000,
 32 |   initialDelay = 1000
 33 | ): Promise<boolean> {
 34 |   // Wait for an initial period to let the service initialize
 35 |   await new Promise((resolve) => setTimeout(resolve, initialDelay));
 36 | 
 37 |   const startTime = Date.now();
 38 |   while (Date.now() - startTime < timeout) {
 39 |     if (isServiceRunning()) {
 40 |       // Wait for an additional short period to ensure service is fully ready
 41 |       await new Promise((resolve) => setTimeout(resolve, 500));
 42 |       return true;
 43 |     }
 44 |     await new Promise((resolve) => setTimeout(resolve, 100));
 45 |   }
 46 |   return false;
 47 | }
 48 | 
 49 | async function main() {
 50 |   switch (command) {
 51 |     case "start":
 52 |       run();
 53 |       break;
 54 |     case "stop":
 55 |       try {
 56 |         const pid = parseInt(readFileSync(PID_FILE, "utf-8"));
 57 |         process.kill(pid);
 58 |         cleanupPidFile();
 59 |         if (existsSync(REFERENCE_COUNT_FILE)) {
 60 |           try {
 61 |             require("fs").unlinkSync(REFERENCE_COUNT_FILE);
 62 |           } catch (e) {
 63 |             // Ignore cleanup errors
 64 |           }
 65 |         }
 66 |         console.log(
 67 |           "claude code router service has been successfully stopped."
 68 |         );
 69 |       } catch (e) {
 70 |         console.log(
 71 |           "Failed to stop the service. It may have already been stopped."
 72 |         );
 73 |         cleanupPidFile();
 74 |       }
 75 |       break;
 76 |     case "status":
 77 |       showStatus();
 78 |       break;
 79 |     case "code":
 80 |       if (!isServiceRunning()) {
 81 |         console.log("Service not running, starting service...");
 82 |         const cliPath = join(__dirname, "cli.js");
 83 |         const startProcess = spawn("node", [cliPath, "start"], {
 84 |           detached: true,
 85 |           stdio: "ignore",
 86 |         });
 87 | 
 88 |         startProcess.on("error", (error) => {
 89 |           console.error("Failed to start service:", error);
 90 |           process.exit(1);
 91 |         });
 92 | 
 93 |         startProcess.unref();
 94 | 
 95 |         if (await waitForService()) {
 96 |           executeCodeCommand(process.argv.slice(3));
 97 |         } else {
 98 |           console.error(
 99 |             "Service startup timeout, please manually run `ccr start` to start the service"
100 |           );
101 |           process.exit(1);
102 |         }
103 |       } else {
104 |         executeCodeCommand(process.argv.slice(3));
105 |       }
106 |       break;
107 |     case "-v":
108 |     case "version":
109 |       console.log(`claude-code-router version: ${version}`);
110 |       break;
111 |     case "-h":
112 |     case "help":
113 |       console.log(HELP_TEXT);
114 |       break;
115 |     default:
116 |       console.log(HELP_TEXT);
117 |       process.exit(1);
118 |   }
119 | }
120 | 
121 | main().catch(console.error);
122 | 


--------------------------------------------------------------------------------
/src/constants.ts:
--------------------------------------------------------------------------------
 1 | import path from "node:path";
 2 | import os from "node:os";
 3 | 
 4 | export const HOME_DIR = path.join(os.homedir(), ".claude-code-router");
 5 | 
 6 | export const CONFIG_FILE = path.join(HOME_DIR, "config.json");
 7 | 
 8 | export const PLUGINS_DIR = path.join(HOME_DIR, "plugins");
 9 | 
10 | export const PID_FILE = path.join(HOME_DIR, '.claude-code-router.pid');
11 | 
12 | export const REFERENCE_COUNT_FILE = path.join(os.tmpdir(), "claude-code-reference-count.txt");
13 | 
14 | 
15 | export const DEFAULT_CONFIG = {
16 |   LOG: false,
17 |   OPENAI_API_KEY: "",
18 |   OPENAI_BASE_URL: "",
19 |   OPENAI_MODEL: "",
20 | };
21 | 


--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
 1 | import { existsSync } from "fs";
 2 | import { writeFile } from "fs/promises";
 3 | import { homedir } from "os";
 4 | import { join } from "path";
 5 | import { initConfig, initDir } from "./utils";
 6 | import { createServer } from "./server";
 7 | import { router } from "./utils/router";
 8 | import {
 9 |   cleanupPidFile,
10 |   isServiceRunning,
11 |   savePid,
12 | } from "./utils/processCheck";
13 | import { CONFIG_FILE } from "./constants";
14 | 
15 | async function initializeClaudeConfig() {
16 |   const homeDir = homedir();
17 |   const configPath = join(homeDir, ".claude.json");
18 |   if (!existsSync(configPath)) {
19 |     const userID = Array.from(
20 |       { length: 64 },
21 |       () => Math.random().toString(16)[2]
22 |     ).join("");
23 |     const configContent = {
24 |       numStartups: 184,
25 |       autoUpdaterStatus: "enabled",
26 |       userID,
27 |       hasCompletedOnboarding: true,
28 |       lastOnboardingVersion: "1.0.17",
29 |       projects: {},
30 |     };
31 |     await writeFile(configPath, JSON.stringify(configContent, null, 2));
32 |   }
33 | }
34 | 
35 | interface RunOptions {
36 |   port?: number;
37 | }
38 | 
39 | async function run(options: RunOptions = {}) {
40 |   // Check if service is already running
41 |   if (isServiceRunning()) {
42 |     console.log("✅ Service is already running in the background.");
43 |     return;
44 |   }
45 | 
46 |   await initializeClaudeConfig();
47 |   await initDir();
48 |   const config = await initConfig();
49 | 
50 |   const port = options.port || 3456;
51 | 
52 |   // Save the PID of the background process
53 |   savePid(process.pid);
54 | 
55 |   // Handle SIGINT (Ctrl+C) to clean up PID file
56 |   process.on("SIGINT", () => {
57 |     console.log("Received SIGINT, cleaning up...");
58 |     cleanupPidFile();
59 |     process.exit(0);
60 |   });
61 | 
62 |   // Handle SIGTERM to clean up PID file
63 |   process.on("SIGTERM", () => {
64 |     cleanupPidFile();
65 |     process.exit(0);
66 |   });
67 | 
68 |   // Use port from environment variable if set (for background process)
69 |   const servicePort = process.env.SERVICE_PORT
70 |     ? parseInt(process.env.SERVICE_PORT)
71 |     : port;
72 |   const server = createServer({
73 |     jsonPath: CONFIG_FILE,
74 |     initialConfig: {
75 |       // ...config,
76 |       providers: config.Providers || config.providers,
77 |       PORT: servicePort,
78 |       LOG_FILE: join(
79 |         homedir(),
80 |         ".claude-code-router",
81 |         "claude-code-router.log"
82 |       ),
83 |     },
84 |   });
85 |   server.addHook("preHandler", async (req, reply) =>
86 |     router(req, reply, config)
87 |   );
88 |   server.start();
89 | }
90 | 
91 | export { run };
92 | // run();
93 | 


--------------------------------------------------------------------------------
/src/server.ts:
--------------------------------------------------------------------------------
1 | import Server from "@musistudio/llms";
2 | 
3 | export const createServer = (config: any): Server => {
4 |   const server = new Server(config);
5 |   return server;
6 | };
7 | 


--------------------------------------------------------------------------------
/src/utils/close.ts:
--------------------------------------------------------------------------------
 1 | import { isServiceRunning, cleanupPidFile, getReferenceCount } from './processCheck';
 2 | import { readFileSync } from 'fs';
 3 | import { HOME_DIR } from '../constants';
 4 | import { join } from 'path';
 5 | 
 6 | export async function closeService() {
 7 |     const PID_FILE = join(HOME_DIR, '.claude-code-router.pid');
 8 |     
 9 |     if (!isServiceRunning()) {
10 |         console.log("No service is currently running.");
11 |         return;
12 |     }
13 | 
14 |     if (getReferenceCount() > 0) {
15 |         return;
16 |     }
17 | 
18 |     try {
19 |         const pid = parseInt(readFileSync(PID_FILE, 'utf-8'));
20 |         process.kill(pid);
21 |         cleanupPidFile();
22 |         console.log("claude code router service has been successfully stopped.");
23 |     } catch (e) {
24 |         console.log("Failed to stop the service. It may have already been stopped.");
25 |         cleanupPidFile();
26 |     }
27 | }
28 | 


--------------------------------------------------------------------------------
/src/utils/codeCommand.ts:
--------------------------------------------------------------------------------
 1 | import { spawn } from "child_process";
 2 | import {
 3 |   incrementReferenceCount,
 4 |   decrementReferenceCount,
 5 | } from "./processCheck";
 6 | import { closeService } from "./close";
 7 | 
 8 | export async function executeCodeCommand(args: string[] = []) {
 9 |   // Set environment variables
10 |   const env = {
11 |     ...process.env,
12 |     ANTHROPIC_AUTH_TOKEN: "test",
13 |     ANTHROPIC_BASE_URL: `http://127.0.0.1:3456`,
14 |     API_TIMEOUT_MS: "600000",
15 |   };
16 | 
17 |   // Increment reference count when command starts
18 |   incrementReferenceCount();
19 | 
20 |   // Execute claude command
21 |   const claudePath = process.env.CLAUDE_PATH || "claude";
22 |   const claudeProcess = spawn(claudePath, args, {
23 |     env,
24 |     stdio: "inherit",
25 |     shell: true,
26 |   });
27 | 
28 |   claudeProcess.on("error", (error) => {
29 |     console.error("Failed to start claude command:", error.message);
30 |     console.log(
31 |       "Make sure Claude Code is installed: npm install -g @anthropic-ai/claude-code"
32 |     );
33 |     decrementReferenceCount();
34 |     process.exit(1);
35 |   });
36 | 
37 |   claudeProcess.on("close", (code) => {
38 |     decrementReferenceCount();
39 |     closeService();
40 |     process.exit(code || 0);
41 |   });
42 | }
43 | 


--------------------------------------------------------------------------------
/src/utils/index.ts:
--------------------------------------------------------------------------------
 1 | import fs from "node:fs/promises";
 2 | import readline from "node:readline";
 3 | import {
 4 |   CONFIG_FILE,
 5 |   DEFAULT_CONFIG,
 6 |   HOME_DIR,
 7 |   PLUGINS_DIR,
 8 | } from "../constants";
 9 | 
10 | const ensureDir = async (dir_path: string) => {
11 |   try {
12 |     await fs.access(dir_path);
13 |   } catch {
14 |     await fs.mkdir(dir_path, { recursive: true });
15 |   }
16 | };
17 | 
18 | export const initDir = async () => {
19 |   await ensureDir(HOME_DIR);
20 |   await ensureDir(PLUGINS_DIR);
21 | };
22 | 
23 | const createReadline = () => {
24 |   return readline.createInterface({
25 |     input: process.stdin,
26 |     output: process.stdout,
27 |   });
28 | };
29 | 
30 | const question = (query: string): Promise<string> => {
31 |   return new Promise((resolve) => {
32 |     const rl = createReadline();
33 |     rl.question(query, (answer) => {
34 |       rl.close();
35 |       resolve(answer);
36 |     });
37 |   });
38 | };
39 | 
40 | const confirm = async (query: string): Promise<boolean> => {
41 |   const answer = await question(query);
42 |   return answer.toLowerCase() !== "n";
43 | };
44 | 
45 | export const readConfigFile = async () => {
46 |   try {
47 |     const config = await fs.readFile(CONFIG_FILE, "utf-8");
48 |     return JSON.parse(config);
49 |   } catch {
50 |     const name = await question("Enter Provider Name: ");
51 |     const apiKey = await question("Enter Provider API KEY: ");
52 |     const baseUrl = await question("Enter Provider URL: ");
53 |     const model = await question("Enter MODEL Name: ");
54 |     const config = Object.assign({}, DEFAULT_CONFIG, {
55 |       Providers: [
56 |         {
57 |           name,
58 |           api_base_url: baseUrl,
59 |           api_key: apiKey,
60 |           models: [model],
61 |         },
62 |       ],
63 |       Router: {
64 |         default: `${name},${model}`,
65 |       },
66 |     });
67 |     await writeConfigFile(config);
68 |     return config;
69 |   }
70 | };
71 | 
72 | export const writeConfigFile = async (config: any) => {
73 |   await ensureDir(HOME_DIR);
74 |   await fs.writeFile(CONFIG_FILE, JSON.stringify(config, null, 2));
75 | };
76 | 
77 | export const initConfig = async () => {
78 |   const config = await readConfigFile();
79 |   Object.assign(process.env, config);
80 |   return config;
81 | };
82 | 


--------------------------------------------------------------------------------
/src/utils/log.ts:
--------------------------------------------------------------------------------
 1 | import fs from "node:fs";
 2 | import path from "node:path";
 3 | import { HOME_DIR } from "../constants";
 4 | 
 5 | const LOG_FILE = path.join(HOME_DIR, "claude-code-router.log");
 6 | 
 7 | // Ensure log directory exists
 8 | if (!fs.existsSync(HOME_DIR)) {
 9 |   fs.mkdirSync(HOME_DIR, { recursive: true });
10 | }
11 | 
12 | export function log(...args: any[]) {
13 |   // Check if logging is enabled via environment variable
14 |   const isLogEnabled = process.env.LOG === "true";
15 | 
16 |   if (!isLogEnabled) {
17 |     return;
18 |   }
19 | 
20 |   const timestamp = new Date().toISOString();
21 |   const logMessage = `[${timestamp}] ${
22 |     Array.isArray(args)
23 |       ? args
24 |           .map((arg) =>
25 |             typeof arg === "object" ? JSON.stringify(arg) : String(arg)
26 |           )
27 |           .join(" ")
28 |       : ""
29 |   }\n`;
30 | 
31 |   // Append to log file
32 |   fs.appendFileSync(LOG_FILE, logMessage, "utf8");
33 | }
34 | 


--------------------------------------------------------------------------------
/src/utils/processCheck.ts:
--------------------------------------------------------------------------------
 1 | import { existsSync, readFileSync, writeFileSync } from 'fs';
 2 | import { PID_FILE, REFERENCE_COUNT_FILE } from '../constants';
 3 | 
 4 | export function incrementReferenceCount() {
 5 |     let count = 0;
 6 |     if (existsSync(REFERENCE_COUNT_FILE)) {
 7 |         count = parseInt(readFileSync(REFERENCE_COUNT_FILE, 'utf-8')) || 0;
 8 |     }
 9 |     count++;
10 |     writeFileSync(REFERENCE_COUNT_FILE, count.toString());
11 | }
12 | 
13 | export function decrementReferenceCount() {
14 |     let count = 0;
15 |     if (existsSync(REFERENCE_COUNT_FILE)) {
16 |         count = parseInt(readFileSync(REFERENCE_COUNT_FILE, 'utf-8')) || 0;
17 |     }
18 |     count = Math.max(0, count - 1);
19 |     writeFileSync(REFERENCE_COUNT_FILE, count.toString());
20 | }
21 | 
22 | export function getReferenceCount(): number {
23 |     if (!existsSync(REFERENCE_COUNT_FILE)) {
24 |         return 0;
25 |     }
26 |     return parseInt(readFileSync(REFERENCE_COUNT_FILE, 'utf-8')) || 0;
27 | }
28 | 
29 | export function isServiceRunning(): boolean {
30 |     if (!existsSync(PID_FILE)) {
31 |         return false;
32 |     }
33 | 
34 |     try {
35 |         const pid = parseInt(readFileSync(PID_FILE, 'utf-8'));
36 |         process.kill(pid, 0);
37 |         return true;
38 |     } catch (e) {
39 |         // Process not running, clean up pid file
40 |         cleanupPidFile();
41 |         return false;
42 |     }
43 | }
44 | 
45 | export function savePid(pid: number) {
46 |     writeFileSync(PID_FILE, pid.toString());
47 | }
48 | 
49 | export function cleanupPidFile() {
50 |     if (existsSync(PID_FILE)) {
51 |         try {
52 |             const fs = require('fs');
53 |             fs.unlinkSync(PID_FILE);
54 |         } catch (e) {
55 |             // Ignore cleanup errors
56 |         }
57 |     }
58 | }
59 | 
60 | export function getServicePid(): number | null {
61 |     if (!existsSync(PID_FILE)) {
62 |         return null;
63 |     }
64 |     
65 |     try {
66 |         const pid = parseInt(readFileSync(PID_FILE, 'utf-8'));
67 |         return isNaN(pid) ? null : pid;
68 |     } catch (e) {
69 |         return null;
70 |     }
71 | }
72 | 
73 | export function getServiceInfo() {
74 |     const pid = getServicePid();
75 |     const running = isServiceRunning();
76 |     
77 |     return {
78 |         running,
79 |         pid,
80 |         port: 3456,
81 |         endpoint: 'http://127.0.0.1:3456',
82 |         pidFile: PID_FILE,
83 |         referenceCount: getReferenceCount()
84 |     };
85 | }
86 | 


--------------------------------------------------------------------------------
/src/utils/router.ts:
--------------------------------------------------------------------------------
 1 | import { MessageCreateParamsBase } from "@anthropic-ai/sdk/resources/messages";
 2 | import { get_encoding } from "tiktoken";
 3 | import { log } from "./log";
 4 | 
 5 | const enc = get_encoding("cl100k_base");
 6 | 
 7 | const getUseModel = (req: any, tokenCount: number, config: any) => {
 8 |   if (req.body.model.includes(",")) {
 9 |     return req.body.model;
10 |   }
11 |   // if tokenCount is greater than 60K, use the long context model
12 |   if (tokenCount > 1000 * 60 && config.Router.longContext) {
13 |     log("Using long context model due to token count:", tokenCount);
14 |     return config.Router.longContext;
15 |   }
16 |   // If the model is claude-3-5-haiku, use the background model
17 |   if (req.body.model?.startsWith("claude-3-5-haiku") && config.Router.background) {
18 |     log("Using background model for ", req.body.model);
19 |     return config.Router.background;
20 |   }
21 |   // if exits thinking, use the think model
22 |   if (req.body.thinking && config.Router.think) {
23 |     log("Using think model for ", req.body.thinking);
24 |     return config.Router.think;
25 |   }
26 |   return config.Router!.default;
27 | };
28 | 
29 | export const router = async (req: any, res: any, config: any) => {
30 |   const { messages, system = [], tools }: MessageCreateParamsBase = req.body;
31 |   try {
32 |     let tokenCount = 0;
33 |     if (Array.isArray(messages)) {
34 |       messages.forEach((message) => {
35 |         if (typeof message.content === "string") {
36 |           tokenCount += enc.encode(message.content).length;
37 |         } else if (Array.isArray(message.content)) {
38 |           message.content.forEach((contentPart) => {
39 |             if (contentPart.type === "text") {
40 |               tokenCount += enc.encode(contentPart.text).length;
41 |             } else if (contentPart.type === "tool_use") {
42 |               tokenCount += enc.encode(
43 |                 JSON.stringify(contentPart.input)
44 |               ).length;
45 |             } else if (contentPart.type === "tool_result") {
46 |               tokenCount += enc.encode(
47 |                 typeof contentPart.content === "string"
48 |                   ? contentPart.content
49 |                   : JSON.stringify(contentPart.content)
50 |               ).length;
51 |             }
52 |           });
53 |         }
54 |       });
55 |     }
56 |     if (typeof system === "string") {
57 |       tokenCount += enc.encode(system).length;
58 |     } else if (Array.isArray(system)) {
59 |       system.forEach((item) => {
60 |         if (item.type !== "text") return;
61 |         if (typeof item.text === "string") {
62 |           tokenCount += enc.encode(item.text).length;
63 |         } else if (Array.isArray(item.text)) {
64 |           item.text.forEach((textPart) => {
65 |             tokenCount += enc.encode(textPart || "").length;
66 |           });
67 |         }
68 |       });
69 |     }
70 |     if (tools) {
71 |       tools.forEach((tool) => {
72 |         if (tool.description) {
73 |           tokenCount += enc.encode(tool.name + tool.description).length;
74 |         }
75 |         if (tool.input_schema) {
76 |           tokenCount += enc.encode(JSON.stringify(tool.input_schema)).length;
77 |         }
78 |       });
79 |     }
80 |     const model = getUseModel(req, tokenCount, config);
81 |     req.body.model = model;
82 |   } catch (error: any) {
83 |     log("Error in router middleware:", error.message);
84 |     req.body.model = config.Router!.default;
85 |   }
86 |   return;
87 | };
88 | 


--------------------------------------------------------------------------------
/src/utils/status.ts:
--------------------------------------------------------------------------------
 1 | import { getServiceInfo } from './processCheck';
 2 | 
 3 | export function showStatus() {
 4 |     const info = getServiceInfo();
 5 |     
 6 |     console.log('\n📊 Claude Code Router Status');
 7 |     console.log('═'.repeat(40));
 8 |     
 9 |     if (info.running) {
10 |         console.log('✅ Status: Running');
11 |         console.log(`🆔 Process ID: ${info.pid}`);
12 |         console.log(`🌐 Port: ${info.port}`);
13 |         console.log(`📡 API Endpoint: ${info.endpoint}`);
14 |         console.log(`📄 PID File: ${info.pidFile}`);
15 |         console.log('');
16 |         console.log('🚀 Ready to use! Run the following commands:');
17 |         console.log('   ccr code    # Start coding with Claude');
18 |         console.log('   ccr stop   # Stop the service');
19 |     } else {
20 |         console.log('❌ Status: Not Running');
21 |         console.log('');
22 |         console.log('💡 To start the service:');
23 |         console.log('   ccr start');
24 |     }
25 |     
26 |     console.log('');
27 | }
28 | 


--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
 1 | {
 2 |   "compilerOptions": {
 3 |     "target": "ES2022",
 4 |     "module": "CommonJS",
 5 |     "outDir": "./dist",
 6 |     "rootDir": "./src",
 7 |     "strict": true,
 8 |     "esModuleInterop": true,
 9 |     "skipLibCheck": true,
10 |     "forceConsistentCasingInFileNames": true,
11 |     "resolveJsonModule": true,
12 |     "moduleResolution": "node",
13 |     "noImplicitAny": true,
14 |     "allowSyntheticDefaultImports": true,
15 |     "sourceMap": true,
16 |     "declaration": true
17 |   },
18 |   "include": ["src/**/*.ts"],
19 |   "exclude": ["node_modules", "dist"]
20 | }
21 | 


--------------------------------------------------------------------------------