kiro-proxy 0.1.18 → 0.2.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/README.md CHANGED
@@ -13,7 +13,7 @@
13
13
  ## 快速开始
14
14
 
15
15
  ```bash
16
- npx @colin3191/kiro-proxy
16
+ npx kiro-proxy
17
17
  ```
18
18
 
19
19
  服务默认监听 `http://localhost:3456`。
@@ -23,6 +23,8 @@ npx @colin3191/kiro-proxy
23
23
  | 环境变量 | 默认值 | 说明 |
24
24
  |----------|--------|------|
25
25
  | `PORT` | `3456` | 监听端口 |
26
+ | `PROXY_API_KEY` | 无 | 设置后所有请求需携带此 key 进行鉴权,未设置则不校验 |
27
+ | `HTTPS_PROXY` | 无 | HTTP/HTTPS 代理地址,如 `http://127.0.0.1:7890` |
26
28
 
27
29
  ## API
28
30
 
@@ -32,40 +34,58 @@ npx @colin3191/kiro-proxy
32
34
  curl http://localhost:3456/v1/models
33
35
  ```
34
36
 
35
- ### POST /v1/chat/completionsOpenAI 兼容
37
+ ### POST /v1/messagesAnthropic 兼容
36
38
 
37
39
  ```bash
38
40
  # 非流式
39
- curl http://localhost:3456/v1/chat/completions \
41
+ curl http://localhost:3456/v1/messages \
40
42
  -H "Content-Type: application/json" \
41
- -d '{"model": "claude-sonnet-4.6", "messages": [{"role": "user", "content": "Hello"}]}'
43
+ -H "x-api-key: any" \
44
+ -d '{"model": "claude-sonnet-4.6", "max_tokens": 1024, "messages": [{"role": "user", "content": "Hello"}]}'
42
45
 
43
46
  # 流式
44
- curl http://localhost:3456/v1/chat/completions \
47
+ curl http://localhost:3456/v1/messages \
45
48
  -H "Content-Type: application/json" \
46
- -d '{"model": "claude-sonnet-4.6", "messages": [{"role": "user", "content": "Hello"}], "stream": true}'
49
+ -H "x-api-key: any" \
50
+ -d '{"model": "claude-sonnet-4.6", "max_tokens": 1024, "messages": [{"role": "user", "content": "Hello"}], "stream": true}'
47
51
  ```
48
52
 
49
- ### POST /v1/messagesAnthropic 兼容
53
+ ### POST /v1/chat/completionsOpenAI 兼容
50
54
 
51
55
  ```bash
52
56
  # 非流式
53
- curl http://localhost:3456/v1/messages \
57
+ curl http://localhost:3456/v1/chat/completions \
54
58
  -H "Content-Type: application/json" \
55
- -H "x-api-key: any" \
56
- -d '{"model": "claude-sonnet-4.6", "max_tokens": 1024, "messages": [{"role": "user", "content": "Hello"}]}'
59
+ -d '{"model": "claude-sonnet-4.6", "messages": [{"role": "user", "content": "Hello"}]}'
57
60
 
58
61
  # 流式
59
- curl http://localhost:3456/v1/messages \
62
+ curl http://localhost:3456/v1/chat/completions \
60
63
  -H "Content-Type: application/json" \
61
- -H "x-api-key: any" \
62
- -d '{"model": "claude-sonnet-4.6", "max_tokens": 1024, "messages": [{"role": "user", "content": "Hello"}], "stream": true}'
64
+ -d '{"model": "claude-sonnet-4.6", "messages": [{"role": "user", "content": "Hello"}], "stream": true}'
63
65
  ```
64
66
 
65
67
  ### GET /health
66
68
 
67
69
  检查 token 状态及过期时间。
68
70
 
71
+ ### GET /credits
72
+
73
+ 查询积分消耗统计,支持 `period` 参数:
74
+
75
+ ```bash
76
+ # 今日消耗(默认)
77
+ curl http://localhost:3456/credits
78
+
79
+ # 最近 7 天
80
+ curl http://localhost:3456/credits?period=7d
81
+
82
+ # 最近 30 天
83
+ curl http://localhost:3456/credits?period=30d
84
+
85
+ # 全部
86
+ curl http://localhost:3456/credits?period=all
87
+ ```
88
+
69
89
  ## 与 Claude Code 集成
70
90
 
71
91
  Claude Code 默认使用 Anthropic 官方 model ID,需要通过环境变量映射到 Q Developer 的 model ID。
@@ -89,6 +109,21 @@ Claude Code 默认使用 Anthropic 官方 model ID,需要通过环境变量映
89
109
 
90
110
  > 注意:不要设置 `ANTHROPIC_MODEL` 环境变量,它会覆盖 `model` 字段,导致上下文窗口等配置失效。
91
111
 
112
+ ## 代理设置
113
+
114
+ 自 2026 年 5 月 1 日起,Kiro 上的 Claude 模型无法在中国大陆及港澳台地区使用。如果遇到 `Invalid model` 错误,请配置代理。
115
+
116
+ > 注意:代理节点需选择其他地区(如新加坡、泰国、韩国等)。
117
+
118
+ 通过环境变量设置 HTTP 代理:
119
+
120
+ ```bash
121
+ # 设置代理后启动
122
+ HTTPS_PROXY=http://127.0.0.1:7890 npx kiro-proxy
123
+ ```
124
+
125
+ 支持的环境变量:`HTTPS_PROXY`、`https_proxy`、`HTTP_PROXY`、`http_proxy`,优先级从左到右。
126
+
92
127
  ## 相关项目
93
128
 
94
129
  - [kiro-web-search](https://github.com/Colin3191/kiro-web-search) — 将 Kiro 内置的联网搜索封装为 MCP server,可在 Claude Code 等客户端中使用
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kiro-proxy",
3
- "version": "0.1.18",
3
+ "version": "0.2.0",
4
4
  "description": "Kiro API proxy with OpenAI and Anthropic compatible endpoints",
5
5
  "type": "module",
6
6
  "bin": {
@@ -15,7 +15,8 @@
15
15
  "token-reader.js",
16
16
  "token-counter.js",
17
17
  "usage-tracker.js",
18
- "logger.js"
18
+ "logger.js",
19
+ "proxy-config.js"
19
20
  ],
20
21
  "repository": {
21
22
  "type": "git",
@@ -23,6 +24,8 @@
23
24
  },
24
25
  "dependencies": {
25
26
  "@aws/codewhisperer-streaming-client": "^1.0.34",
26
- "express": "^4.21.0"
27
+ "express": "^4.21.0",
28
+ "https-proxy-agent": "^7.0.0",
29
+ "undici": "^6.19.0"
27
30
  }
28
31
  }
@@ -0,0 +1,21 @@
1
+ import { setGlobalDispatcher, EnvHttpProxyAgent } from 'undici';
2
+ import { HttpsProxyAgent } from 'https-proxy-agent';
3
+
4
+ export function getProxyUrl() {
5
+ return process.env.HTTPS_PROXY || process.env.https_proxy ||
6
+ process.env.HTTP_PROXY || process.env.http_proxy || '';
7
+ }
8
+
9
+ export function initGlobalProxy() {
10
+ const proxyUrl = getProxyUrl();
11
+ if (proxyUrl) {
12
+ setGlobalDispatcher(new EnvHttpProxyAgent());
13
+ }
14
+ return proxyUrl;
15
+ }
16
+
17
+ export function createProxyAgent() {
18
+ const proxyUrl = getProxyUrl();
19
+ if (!proxyUrl) return undefined;
20
+ return new HttpsProxyAgent(proxyUrl);
21
+ }
package/q-client.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import { CodeWhispererStreaming, GenerateAssistantResponseCommand } from '@aws/codewhisperer-streaming-client';
2
2
  import crypto from 'crypto';
3
3
  import os from 'os';
4
+ import { createProxyAgent } from './proxy-config.js';
4
5
 
5
6
  // region → endpoint 映射
6
7
  const REGION_ENDPOINTS = {
@@ -71,12 +72,19 @@ export function createClient(accessToken, { endpoint, region, authMethod, profil
71
72
  const finalRegion = region || arnRegion || DEFAULT_REGION;
72
73
  const finalEndpoint = endpoint || endpointForRegion(finalRegion);
73
74
 
74
- const client = new CodeWhispererStreaming({
75
+ const clientConfig = {
75
76
  region: finalRegion,
76
77
  endpoint: finalEndpoint,
77
78
  token: { token: accessToken },
78
79
  customUserAgent: buildUserAgent(machineId),
79
- });
80
+ };
81
+
82
+ const proxyAgent = createProxyAgent();
83
+ if (proxyAgent) {
84
+ clientConfig.requestHandler = { httpsAgent: proxyAgent };
85
+ }
86
+
87
+ const client = new CodeWhispererStreaming(clientConfig);
80
88
  addRequiredHeaders(client, { authMethod, provider });
81
89
  return client;
82
90
  }
package/server.js CHANGED
@@ -3,14 +3,29 @@ import express from 'express';
3
3
  import crypto from 'crypto';
4
4
  import { getAccessToken } from './token-reader.js';
5
5
  import { createClient, chat, chatStream, listAvailableModels } from './q-client.js';
6
- import { c, log, logSummary, reqId, tagError } from './logger.js';
6
+ import { c, log, tagLog, logSummary, reqId, tagError } from './logger.js';
7
7
  import { countMessages, countContent } from './token-counter.js';
8
8
  import { recordUsage, queryUsage, todaySummary } from './usage-tracker.js';
9
+ import { initGlobalProxy } from './proxy-config.js';
10
+
11
+ const proxyUrl = initGlobalProxy();
12
+ if (proxyUrl) tagLog('proxy', `Using proxy: ${proxyUrl}`);
9
13
 
10
14
  const app = express();
11
15
  app.use(express.json({ limit: '10mb' }));
12
16
 
13
17
  const PORT = process.env.PORT || 3456;
18
+ const PROXY_API_KEY = process.env.PROXY_API_KEY;
19
+
20
+ function authMiddleware(req, res, next) {
21
+ if (!PROXY_API_KEY) return next();
22
+ const auth = req.headers['authorization'];
23
+ const token = auth?.startsWith('Bearer ') ? auth.slice(7) : null;
24
+ if (token === PROXY_API_KEY) return next();
25
+ res.status(401).json({ type: 'error', error: { type: 'authentication_error', message: 'Invalid or missing API key' } });
26
+ }
27
+
28
+ app.use(authMiddleware);
14
29
 
15
30
  let cachedClient = null;
16
31
  let cachedToken = null;
@@ -357,6 +372,7 @@ app.listen(PORT, async () => {
357
372
  console.log(` ${c.gray}OpenAI: ${c.reset} http://localhost:${PORT}/v1/chat/completions`);
358
373
  console.log(` ${c.gray}Models: ${c.reset} http://localhost:${PORT}/v1/models`);
359
374
  console.log(` ${c.gray}Credits: ${c.reset} http://localhost:${PORT}/credits`);
375
+ console.log(` ${c.gray}Auth: ${c.reset} ${PROXY_API_KEY ? `${c.green}enabled${c.reset} (PROXY_API_KEY)` : `${c.yellow}disabled${c.reset} (no PROXY_API_KEY set)`}`);
360
376
  try {
361
377
  const t = await getAccessToken();
362
378
  console.log(` ${c.gray}Provider: ${c.yellow}${t.provider || 'unknown'}${c.reset}, Expires: ${c.dim}${t.expiresAt || 'unknown'}${c.reset}`);