@sunflower0305/claude-proxy 1.2.0 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +8 -1
- package/CHANGELOG.md +12 -0
- package/README.md +15 -5
- package/dist/proxy.d.ts +1 -0
- package/dist/proxy.js +46 -5
- package/package.json +4 -3
package/.env.example
CHANGED
|
@@ -1,15 +1,20 @@
|
|
|
1
|
-
# Choose your provider: qwen | deepseek | glm | minimax | kimi
|
|
1
|
+
# Choose your provider: qwen | deepseek | glm | minimax | kimi | mimo
|
|
2
2
|
PROVIDER=deepseek
|
|
3
3
|
|
|
4
4
|
# Proxy server port (default: 8080)
|
|
5
5
|
PROXY_PORT=8080
|
|
6
6
|
|
|
7
|
+
# Optional local proxy token.
|
|
8
|
+
# When set, clients must send this value as x-api-key or Authorization: Bearer.
|
|
9
|
+
# PROXY_API_KEY=your-local-proxy-token
|
|
10
|
+
|
|
7
11
|
# API keys - set the one(s) you need
|
|
8
12
|
QWEN_API_KEY=your-qwen-api-key
|
|
9
13
|
DEEPSEEK_API_KEY=your-deepseek-key
|
|
10
14
|
GLM_API_KEY=your-glm-key
|
|
11
15
|
MINIMAX_API_KEY=your-minimax-key
|
|
12
16
|
KIMI_API_KEY=your-kimi-key
|
|
17
|
+
MIMO_API_KEY=your-mimo-key
|
|
13
18
|
|
|
14
19
|
# Optional model overrides
|
|
15
20
|
QWEN_MODEL=qwen-plus
|
|
@@ -17,6 +22,7 @@ DEEPSEEK_MODEL=deepseek-v4-pro
|
|
|
17
22
|
GLM_MODEL=glm-5.1
|
|
18
23
|
MINIMAX_MODEL=MiniMax-M2.7-highspeed
|
|
19
24
|
KIMI_MODEL=kimi-k2.6
|
|
25
|
+
MIMO_MODEL=mimo-v2.5-pro
|
|
20
26
|
|
|
21
27
|
# Optional upstream Anthropic-compatible base URL overrides
|
|
22
28
|
# QWEN_ANTHROPIC_BASE_URL=https://dashscope.aliyuncs.com/apps/anthropic
|
|
@@ -24,3 +30,4 @@ KIMI_MODEL=kimi-k2.6
|
|
|
24
30
|
# GLM_ANTHROPIC_BASE_URL=https://open.bigmodel.cn/api/anthropic
|
|
25
31
|
# MINIMAX_ANTHROPIC_BASE_URL=https://api.minimaxi.com/anthropic
|
|
26
32
|
# KIMI_ANTHROPIC_BASE_URL=https://api.moonshot.cn/anthropic
|
|
33
|
+
# MIMO_ANTHROPIC_BASE_URL=https://api.xiaomimimo.com/anthropic
|
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## [1.3.0] - 2026-05-06
|
|
6
|
+
|
|
7
|
+
Minor release of `@sunflower0305/claude-proxy`.
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- Anthropic-compatible proxy support for MIMO through the `mimo` provider
|
|
12
|
+
- `MIMO_API_KEY`, `MIMO_MODEL`, and `MIMO_ANTHROPIC_BASE_URL` configuration
|
|
13
|
+
- local integration coverage for MIMO provider switching, model mapping, and non-streaming request proxying
|
|
14
|
+
|
|
15
|
+
Detailed release notes: [docs/releases/1.3.0.md](docs/releases/1.3.0.md)
|
|
16
|
+
|
|
5
17
|
## [1.2.0] - 2026-05-01
|
|
6
18
|
|
|
7
19
|
Minor release of `@sunflower0305/claude-proxy`.
|
package/README.md
CHANGED
|
@@ -4,13 +4,13 @@
|
|
|
4
4
|
[](https://github.com/sunflower0305/claude-proxy/actions/workflows/cd.yml)
|
|
5
5
|
[](https://coveralls.io/github/sunflower0305/claude-proxy?branch=master)
|
|
6
6
|
[](https://www.npmjs.com/package/@sunflower0305/claude-proxy)
|
|
7
|
-
[](https://www.npmjs.com/package/@sunflower0305/claude-proxy)
|
|
8
8
|
[](https://github.com/sunflower0305/claude-proxy/stargazers)
|
|
9
9
|
[](https://github.com/sunflower0305/claude-proxy/blob/master/LICENSE)
|
|
10
10
|
|
|
11
11
|
`claude-proxy` is published on npm as `@sunflower0305/claude-proxy`. It is a lightweight Express proxy that lets Claude Code or the Claude Agent SDK talk to domestic Chinese LLM providers through Anthropic-compatible `/v1/messages` endpoints.
|
|
12
12
|
|
|
13
|
-
It currently supports `qwen`, `deepseek`, `glm`, `minimax`, and `
|
|
13
|
+
It currently supports `qwen`, `deepseek`, `glm`, `minimax`, `kimi`, and `mimo`.
|
|
14
14
|
|
|
15
15
|
## Install
|
|
16
16
|
|
|
@@ -38,6 +38,8 @@ Example `.env`:
|
|
|
38
38
|
```dotenv
|
|
39
39
|
PROVIDER=deepseek
|
|
40
40
|
PROXY_PORT=8080
|
|
41
|
+
# Optional: require clients to send this token to write endpoints.
|
|
42
|
+
# PROXY_API_KEY=your-local-proxy-token
|
|
41
43
|
DEEPSEEK_API_KEY=your-deepseek-api-key
|
|
42
44
|
DEEPSEEK_MODEL=deepseek-v4-pro
|
|
43
45
|
```
|
|
@@ -48,13 +50,15 @@ Available variables:
|
|
|
48
50
|
| ------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------- |
|
|
49
51
|
| `PROVIDER` | Active provider. Defaults to `deepseek`. |
|
|
50
52
|
| `PROXY_PORT` | Local server port. Defaults to `8080`. |
|
|
53
|
+
| `PROXY_API_KEY` | Optional local proxy token for `POST /v1/messages` and `POST /api/provider`. |
|
|
51
54
|
| `QWEN_API_KEY` | API key for Qwen. |
|
|
52
55
|
| `DEEPSEEK_API_KEY` | API key for DeepSeek. |
|
|
53
56
|
| `GLM_API_KEY` | API key for GLM. |
|
|
54
57
|
| `MINIMAX_API_KEY` | API key for MiniMax. |
|
|
55
58
|
| `KIMI_API_KEY` | API key for Kimi. |
|
|
56
|
-
| `
|
|
57
|
-
| `
|
|
59
|
+
| `MIMO_API_KEY` | API key for MIMO. |
|
|
60
|
+
| `QWEN_ANTHROPIC_BASE_URL`, `DEEPSEEK_ANTHROPIC_BASE_URL`, `GLM_ANTHROPIC_BASE_URL`, `MINIMAX_ANTHROPIC_BASE_URL`, `KIMI_ANTHROPIC_BASE_URL`, `MIMO_ANTHROPIC_BASE_URL` | Override the upstream Anthropic-compatible base URL for a provider. |
|
|
61
|
+
| `QWEN_MODEL`, `DEEPSEEK_MODEL`, `GLM_MODEL`, `MINIMAX_MODEL`, `KIMI_MODEL`, `MIMO_MODEL` | Override the default upstream model for a provider. |
|
|
58
62
|
|
|
59
63
|
Provider defaults:
|
|
60
64
|
|
|
@@ -65,6 +69,7 @@ Provider defaults:
|
|
|
65
69
|
| `glm` | `GLM_MODEL` | `glm-5.1` |
|
|
66
70
|
| `minimax` | `MINIMAX_MODEL` | `MiniMax-M2.7-highspeed` |
|
|
67
71
|
| `kimi` | `KIMI_MODEL` | `kimi-k2.6` |
|
|
72
|
+
| `mimo` | `MIMO_MODEL` | `mimo-v2.5-pro` |
|
|
68
73
|
|
|
69
74
|
You can use the bundled example as a starting point:
|
|
70
75
|
|
|
@@ -89,6 +94,11 @@ export ANTHROPIC_BASE_URL=http://localhost:8080
|
|
|
89
94
|
export ANTHROPIC_API_KEY=any-string-works
|
|
90
95
|
```
|
|
91
96
|
|
|
97
|
+
If you set `PROXY_API_KEY`, set the client `ANTHROPIC_API_KEY` to the same
|
|
98
|
+
value. The proxy accepts it through either `x-api-key` or
|
|
99
|
+
`Authorization: Bearer`. If `PROXY_API_KEY` is not set, the local proxy does not
|
|
100
|
+
validate the client API key and any non-empty string can be used.
|
|
101
|
+
|
|
92
102
|
Example SDK usage:
|
|
93
103
|
|
|
94
104
|
```ts
|
|
@@ -96,7 +106,7 @@ import Anthropic from "@anthropic-ai/sdk";
|
|
|
96
106
|
|
|
97
107
|
const client = new Anthropic({
|
|
98
108
|
baseURL: "http://localhost:8080",
|
|
99
|
-
apiKey: "any-string",
|
|
109
|
+
apiKey: process.env.PROXY_API_KEY || "any-string",
|
|
100
110
|
});
|
|
101
111
|
```
|
|
102
112
|
|
package/dist/proxy.d.ts
CHANGED
package/dist/proxy.js
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
* Usage:
|
|
9
9
|
* export ANTHROPIC_BASE_URL=http://localhost:8080
|
|
10
10
|
* export ANTHROPIC_API_KEY=any-key-works
|
|
11
|
+
* # If PROXY_API_KEY is set, use that same value instead.
|
|
11
12
|
*/
|
|
12
13
|
import express from "express";
|
|
13
14
|
import { existsSync, realpathSync } from "node:fs";
|
|
@@ -67,12 +68,20 @@ const PROVIDERS = {
|
|
|
67
68
|
apiKey: process.env.KIMI_API_KEY || "",
|
|
68
69
|
model: pickEnv("KIMI_MODEL") || "kimi-k2.6",
|
|
69
70
|
},
|
|
71
|
+
mimo: {
|
|
72
|
+
baseUrl: pickEnv("MIMO_ANTHROPIC_BASE_URL") ||
|
|
73
|
+
"https://api.xiaomimimo.com/anthropic",
|
|
74
|
+
apiKey: process.env.MIMO_API_KEY || "",
|
|
75
|
+
model: pickEnv("MIMO_MODEL") || "mimo-v2.5-pro",
|
|
76
|
+
},
|
|
70
77
|
};
|
|
71
78
|
function isProviderKey(value) {
|
|
72
79
|
return Boolean(value && value in PROVIDERS);
|
|
73
80
|
}
|
|
74
81
|
function getInitialProvider() {
|
|
75
|
-
return isProviderKey(process.env.PROVIDER)
|
|
82
|
+
return isProviderKey(process.env.PROVIDER)
|
|
83
|
+
? process.env.PROVIDER
|
|
84
|
+
: "deepseek";
|
|
76
85
|
}
|
|
77
86
|
function getProviderConfig(provider) {
|
|
78
87
|
return PROVIDERS[provider] || PROVIDERS.deepseek;
|
|
@@ -123,6 +132,19 @@ function createProxyError(message) {
|
|
|
123
132
|
},
|
|
124
133
|
};
|
|
125
134
|
}
|
|
135
|
+
function createAuthenticationError() {
|
|
136
|
+
return {
|
|
137
|
+
type: "error",
|
|
138
|
+
error: {
|
|
139
|
+
type: "authentication_error",
|
|
140
|
+
message: "Invalid API key",
|
|
141
|
+
},
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
function getBearerToken(authorization) {
|
|
145
|
+
const match = authorization?.match(/^Bearer\s+(.+)$/i);
|
|
146
|
+
return match?.[1]?.trim() || undefined;
|
|
147
|
+
}
|
|
126
148
|
function inferProviderFromModel(model) {
|
|
127
149
|
if (!model)
|
|
128
150
|
return undefined;
|
|
@@ -149,6 +171,7 @@ function logTimingEvent(trace, phase, extra = {}) {
|
|
|
149
171
|
export function createApp() {
|
|
150
172
|
let currentProvider = getInitialProvider();
|
|
151
173
|
let requestSequence = 0;
|
|
174
|
+
const proxyApiKey = pickEnv("PROXY_API_KEY");
|
|
152
175
|
function getConfig(provider = currentProvider) {
|
|
153
176
|
return getProviderConfig(provider);
|
|
154
177
|
}
|
|
@@ -180,6 +203,21 @@ export function createApp() {
|
|
|
180
203
|
startedAt: Date.now(),
|
|
181
204
|
};
|
|
182
205
|
}
|
|
206
|
+
function requireProxyApiKey(req, res, next) {
|
|
207
|
+
if (!proxyApiKey) {
|
|
208
|
+
next();
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
const clientTokens = [
|
|
212
|
+
getHeaderValue(req.headers["x-api-key"])?.trim(),
|
|
213
|
+
getBearerToken(getHeaderValue(req.headers.authorization)),
|
|
214
|
+
].filter((token) => Boolean(token));
|
|
215
|
+
if (clientTokens.includes(proxyApiKey)) {
|
|
216
|
+
next();
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
res.status(401).json(createAuthenticationError());
|
|
220
|
+
}
|
|
183
221
|
async function handleNonStreamingRequest(req, res) {
|
|
184
222
|
const config = getConfig();
|
|
185
223
|
const targetModel = getTargetModel(req.body?.model);
|
|
@@ -308,7 +346,7 @@ export function createApp() {
|
|
|
308
346
|
}
|
|
309
347
|
}
|
|
310
348
|
const app = express();
|
|
311
|
-
app.use(express.json({ limit: "
|
|
349
|
+
app.use(express.json({ limit: "32mb" }));
|
|
312
350
|
app.get("/", (_req, res) => {
|
|
313
351
|
const config = getConfig();
|
|
314
352
|
res.json({
|
|
@@ -324,7 +362,7 @@ export function createApp() {
|
|
|
324
362
|
},
|
|
325
363
|
});
|
|
326
364
|
});
|
|
327
|
-
app.post("/v1/messages", async (req, res) => {
|
|
365
|
+
app.post("/v1/messages", requireProxyApiKey, async (req, res) => {
|
|
328
366
|
if (req.body?.stream) {
|
|
329
367
|
await handleStreamingRequest(req, res);
|
|
330
368
|
return;
|
|
@@ -353,7 +391,7 @@ export function createApp() {
|
|
|
353
391
|
availableProviders: Object.keys(PROVIDERS),
|
|
354
392
|
});
|
|
355
393
|
});
|
|
356
|
-
app.post("/api/provider", (req, res) => {
|
|
394
|
+
app.post("/api/provider", requireProxyApiKey, (req, res) => {
|
|
357
395
|
const { provider, model } = (req.body ?? {});
|
|
358
396
|
const targetProvider = provider ?? inferProviderFromModel(model);
|
|
359
397
|
if (!isProviderKey(targetProvider)) {
|
|
@@ -397,6 +435,9 @@ function isMainModule() {
|
|
|
397
435
|
if (isMainModule()) {
|
|
398
436
|
const initialProvider = getInitialProvider();
|
|
399
437
|
const initialConfig = getProviderConfig(initialProvider);
|
|
438
|
+
const clientApiKeyHint = pickEnv("PROXY_API_KEY")
|
|
439
|
+
? "same value as PROXY_API_KEY"
|
|
440
|
+
: "any-string-works";
|
|
400
441
|
if (!initialConfig.apiKey) {
|
|
401
442
|
console.warn(`Warning: API key not configured for provider: ${initialProvider}`);
|
|
402
443
|
console.warn("Please set the appropriate environment variable in .env");
|
|
@@ -413,7 +454,7 @@ if (isMainModule()) {
|
|
|
413
454
|
╠═══════════════════════════════════════════════════════╣
|
|
414
455
|
║ Set these env vars in your app: ║
|
|
415
456
|
║ ANTHROPIC_BASE_URL=http://localhost:${PORT}
|
|
416
|
-
║ ANTHROPIC_API_KEY
|
|
457
|
+
║ ANTHROPIC_API_KEY=${clientApiKeyHint}
|
|
417
458
|
╚═══════════════════════════════════════════════════════╝
|
|
418
459
|
`);
|
|
419
460
|
});
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sunflower0305/claude-proxy",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"description": "A proxy that lets Claude Agent SDK use domestic Chinese LLMs (DeepSeek, Qwen, GLM, MiniMax) as backend",
|
|
5
|
+
"description": "A proxy that lets Claude Agent SDK use domestic Chinese LLMs (DeepSeek, Qwen, GLM, MiniMax, Kimi, MIMO) as backend",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"main": "./dist/proxy.js",
|
|
8
8
|
"types": "./dist/proxy.d.ts",
|
|
@@ -46,7 +46,8 @@
|
|
|
46
46
|
"deepseek",
|
|
47
47
|
"glm",
|
|
48
48
|
"minimax",
|
|
49
|
-
"kimi"
|
|
49
|
+
"kimi",
|
|
50
|
+
"mimo"
|
|
50
51
|
],
|
|
51
52
|
"scripts": {
|
|
52
53
|
"dev": "tsx watch src/proxy.ts",
|