KiroProxy User commited on
Commit
0edbd7b
·
1 Parent(s): 0e30efb

chore: repo cleanup and maintenance

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. .dockerignore +13 -0
  2. {KiroProxy/.github → .github}/workflows/build.yml +0 -0
  3. KiroProxy/.gitignore → .gitignore +1 -0
  4. .orchids/orchids.json +0 -9
  5. CAPTURE_GUIDE.md +37 -0
  6. Dockerfile +2 -2
  7. KiroProxy/CAPTURE_GUIDE.md +0 -0
  8. KiroProxy/README.md +0 -425
  9. KiroProxy/kiro_proxy/main_legacy.py +0 -694
  10. KiroProxy/legacy/kiro_proxy.py +0 -313
  11. KiroProxy/requirements.txt +0 -13
  12. KiroProxy/run.py +0 -14
  13. KiroProxy/scripts/capture_kiro.py +0 -139
  14. KiroProxy/scripts/proxy_server.py +0 -135
  15. README.md +364 -59
  16. README_HF.md +0 -9
  17. {KiroProxy/assets → assets}/icon.iconset/icon_128x128.png +0 -0
  18. {KiroProxy/assets → assets}/icon.iconset/icon_16x16.png +0 -0
  19. {KiroProxy/assets → assets}/icon.iconset/icon_256x256.png +0 -0
  20. {KiroProxy/assets → assets}/icon.iconset/icon_32x32.png +0 -0
  21. {KiroProxy/assets → assets}/icon.iconset/icon_512x512.png +0 -0
  22. {KiroProxy/assets → assets}/icon.iconset/icon_64x64.png +0 -0
  23. {KiroProxy/assets → assets}/icon.png +0 -0
  24. {KiroProxy/assets → assets}/icon.svg +0 -0
  25. KiroProxy/build.py → build.py +0 -0
  26. {KiroProxy/examples → examples}/quota_display_example.py +0 -0
  27. {KiroProxy/examples → examples}/test_quota_display.html +0 -0
  28. KiroProxy/kiro.svg → kiro.svg +0 -0
  29. {KiroProxy/kiro_proxy → kiro_proxy}/__init__.py +0 -0
  30. {KiroProxy/kiro_proxy → kiro_proxy}/__main__.py +0 -0
  31. {KiroProxy/kiro_proxy → kiro_proxy}/auth/__init__.py +0 -0
  32. {KiroProxy/kiro_proxy → kiro_proxy}/auth/device_flow.py +0 -0
  33. {KiroProxy/kiro_proxy → kiro_proxy}/cli.py +0 -0
  34. {KiroProxy/kiro_proxy → kiro_proxy}/config.py +0 -0
  35. {KiroProxy/kiro_proxy → kiro_proxy}/converters/__init__.py +0 -0
  36. {KiroProxy/kiro_proxy → kiro_proxy}/core/__init__.py +0 -0
  37. {KiroProxy/kiro_proxy → kiro_proxy}/core/account.py +0 -0
  38. {KiroProxy/kiro_proxy → kiro_proxy}/core/account_selector.py +0 -0
  39. {KiroProxy/kiro_proxy → kiro_proxy}/core/admin_auth.py +0 -0
  40. {KiroProxy/kiro_proxy → kiro_proxy}/core/auth_middleware.py +0 -0
  41. {KiroProxy/kiro_proxy → kiro_proxy}/core/browser.py +0 -0
  42. {KiroProxy/kiro_proxy → kiro_proxy}/core/database.py +0 -0
  43. {KiroProxy/kiro_proxy → kiro_proxy}/core/error_handler.py +0 -0
  44. {KiroProxy/kiro_proxy → kiro_proxy}/core/flow_monitor.py +0 -0
  45. {KiroProxy/kiro_proxy → kiro_proxy}/core/history_manager.py +0 -0
  46. {KiroProxy/kiro_proxy → kiro_proxy}/core/kiro_api.py +0 -0
  47. {KiroProxy/kiro_proxy → kiro_proxy}/core/persistence.py +0 -0
  48. {KiroProxy/kiro_proxy → kiro_proxy}/core/protocol_handler.py +0 -0
  49. {KiroProxy/kiro_proxy → kiro_proxy}/core/quota_cache.py +0 -0
  50. {KiroProxy/kiro_proxy → kiro_proxy}/core/quota_scheduler.py +0 -0
.dockerignore ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .git
2
+ .gitignore
3
+ __pycache__/
4
+ *.pyc
5
+ .pytest_cache/
6
+ .hypothesis/
7
+ .env
8
+ dist/
9
+ build/
10
+ release/
11
+ .venv/
12
+ venv/
13
+ kiro_requests/
{KiroProxy/.github → .github}/workflows/build.yml RENAMED
File without changes
KiroProxy/.gitignore → .gitignore RENAMED
@@ -40,6 +40,7 @@ flows
40
  flows_*
41
  traffic.mitm
42
  *.mitm
 
43
  analyze_har.py
44
  parse_*.py
45
  *_analysis.txt
 
40
  flows_*
41
  traffic.mitm
42
  *.mitm
43
+ kiro_requests/
44
  analyze_har.py
45
  parse_*.py
46
  *_analysis.txt
.orchids/orchids.json DELETED
@@ -1,9 +0,0 @@
1
- {
2
- "projectId": "c1b5887f-dbf9-4c85-bec3-696c4662d56c",
3
- "createdAt": 1768697452308,
4
- "version": "1.0",
5
- "startupCommands": [
6
- "bun install; bun dev"
7
- ],
8
- "templateId": "nextjs"
9
- }
 
 
 
 
 
 
 
 
 
 
CAPTURE_GUIDE.md ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Kiro 请求抓包指南
2
+
3
+ 本项目提供 `scripts/capture_kiro.py`(mitmproxy addon)用于抓取 Kiro IDE 与 AWS 端点的请求/响应,便于调试协议与字段。
4
+
5
+ ## 安装依赖
6
+
7
+ ```bash
8
+ pip install mitmproxy
9
+ ```
10
+
11
+ ## 运行 mitmproxy
12
+
13
+ 带 UI:
14
+
15
+ ```bash
16
+ mitmproxy --mode regular@8888 -s scripts/capture_kiro.py
17
+ ```
18
+
19
+ 无 UI:
20
+
21
+ ```bash
22
+ mitmdump --mode regular@8888 -s scripts/capture_kiro.py
23
+ ```
24
+
25
+ ## 配置代理与证书
26
+
27
+ - 将系统或 Kiro IDE 的 HTTP/HTTPS 代理设置为 `127.0.0.1:8888`
28
+ - 如需解密 HTTPS,安装 mitmproxy CA 证书:访问 `http://mitm.it`
29
+
30
+ ## 输出文件
31
+
32
+ 抓到的请求/响应会输出到 `kiro_requests/`:
33
+
34
+ - `*_request.json`
35
+ - `*_response.json`
36
+
37
+ `kiro_requests/` 已在 `.gitignore` 中忽略。
Dockerfile CHANGED
@@ -23,7 +23,7 @@ COPY requirements.txt .
23
  RUN pip install --no-cache-dir -r requirements.txt
24
 
25
  # 复制项目文件
26
- COPY KiroProxy/ ./KiroProxy/
27
 
28
  # 创建数据目录
29
  RUN mkdir -p /app/data
@@ -36,4 +36,4 @@ HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
36
  CMD curl -f http://localhost:7860/api/status || exit 1
37
 
38
  # 启动命令
39
- CMD ["python", "KiroProxy/run.py", "7860"]
 
23
  RUN pip install --no-cache-dir -r requirements.txt
24
 
25
  # 复制项目文件
26
+ COPY . .
27
 
28
  # 创建数据目录
29
  RUN mkdir -p /app/data
 
36
  CMD curl -f http://localhost:7860/api/status || exit 1
37
 
38
  # 启动命令
39
+ CMD ["python", "run.py", "7860"]
KiroProxy/CAPTURE_GUIDE.md DELETED
File without changes
KiroProxy/README.md DELETED
@@ -1,425 +0,0 @@
1
- <p align="center">
2
- <img src="assets/icon.svg" width="80" height="96" alt="Kiro Proxy">
3
- </p>
4
-
5
- <h1 align="center">Kiro API Proxy</h1>
6
-
7
- <p align="center">
8
- Kiro IDE API 反向代理服务器,支持多账号轮询、Token 自动刷新、配额管理
9
- </p>
10
-
11
- <p align="center">
12
- <a href="#功能特性">功能</a> •
13
- <a href="#快速开始">快速开始</a> •
14
- <a href="#cli-配置">CLI 配置</a> •
15
- <a href="#api-端点">API</a> •
16
- <a href="#许可证">许可证</a>
17
- </p>
18
-
19
- ---
20
-
21
- > **⚠️ 测试说明**
22
- >
23
- > 本项目支持 **Claude Code**、**Codex CLI**、**Gemini CLI** 三种客户端,工具调用功能已全面支持。
24
-
25
- ## 功能特性
26
-
27
- ### 核心功能
28
- - **多协议支持** - OpenAI / Anthropic / Gemini 三种协议兼容
29
- - **完整工具调用** - 三种协议的工具调用功能全面支持
30
- - **图片理解** - 支持 Claude Code / Codex CLI 图片输入
31
- - **网络搜索** - 支持 Claude Code / Codex CLI 网络搜索工具
32
- - **思考功能** - 支持 Claude 的扩展思考功能(Extended Thinking)
33
- - **多账号轮询(默认随机)** - 每次请求随机切换账号,分散压力,避免单账号 RPM 过高
34
- - **会话粘性(可选)** - 非 `random` 策略下,同一会话 60 秒内使用同一账号,保持上下文
35
- - **Web UI** - 简洁的管理界面,支持监控、日志、设置
36
-
37
- ### v1.7.1 新功能
38
- - **Windows 支持补强** - 注册表浏览器检测 + PATH 回退,兼容便携版
39
- - **打包资源修复** - PyInstaller 打包后可正常加载图标与内置文档
40
- - **Token 扫描稳定性** - Windows 路径编码处理修复
41
-
42
- ### v1.6.3 新功能
43
- - **命令行工具 (CLI)** - 无 GUI 服务器也能轻松管理
44
- - `python run.py accounts list` - 列出账号
45
- - `python run.py accounts export/import` - 导出/导入账号
46
- - `python run.py accounts add` - 交互式添加 Token
47
- - `python run.py accounts scan` - 扫描本地 Token
48
- - `python run.py login google/github` - 命令行登录
49
- - `python run.py login remote` - 生成远程登录链接
50
- - **远程登录链接** - 在有浏览器的机器上完成授权,Token 自动同步
51
- - **账号导入导出** - 跨机器迁移账号配置
52
- - **手动添加 Token** - 直接粘贴 accessToken/refreshToken
53
-
54
- ### v1.6.2 新功能
55
- - **Codex CLI 完整支持** - 使用 OpenAI Responses API (`/v1/responses`)
56
- - 完整工具调用支持(shell、file 等所有工具)
57
- - 图片输入支持(`input_image` 类型)
58
- - 网络搜索支持(`web_search` 工具)
59
- - 错误代码映射(rate_limit、context_length 等)
60
- - **Claude Code 增强** - 图片理解和网络搜索完整支持
61
- - 支持 Anthropic 和 OpenAI 两种图片格式
62
- - 支持 `web_search` / `web_search_20250305` 工具
63
-
64
- ### v1.6.1 新功能
65
- - **请求限速** - 通过限制请求频率降低账号封禁风险
66
- - 每账号最小请求间隔
67
- - 每账号每分钟最大请求数
68
- - 全局每分钟最大请求数
69
- - WebUI 设置页面可配置
70
- - **账号封禁检测** - 自动检测 TEMPORARILY_SUSPENDED 错误
71
- - 友好的错误日志输出
72
- - 自动禁用被封禁账号
73
- - 自动切换到其他可用账号
74
- - **统一错误处理** - 三种协议使用统一的错误分类和处理
75
-
76
- ### v1.6.0 功能
77
- - **历史消息管理** - 4 种策略处理对话长度限制,可自由组合
78
- - 自动截断:发送前优先保留最新上下文并摘要前文,必要时按数量/字符数截断
79
- - 智能摘要:用 AI 生成早期对话摘要,保留关键信息
80
- - 摘要缓存:历史变化不大时复用最近摘要,减少重复 LLM 调用(默认启用)
81
- - 错误重试:遇到长度错误时自动截断重试(默认启用)
82
- - 预估检测:预估 token 数量,超限预先截断
83
- - **Gemini 工具调用** - 完整支持 functionDeclarations/functionCall/functionResponse
84
- - **设置页面** - WebUI 新增设置标签页,可配置历史消息管理策略
85
-
86
- ### v1.5.0 功能
87
- - **用量查询** - 查询账号配额使用情况,显示已用/余额/使用率
88
- - **多登录方式** - 支持 Google / GitHub / AWS Builder ID 三种登录方式
89
- - **流量监控** - 完整的 LLM 请求监控,支持搜索、过滤、导出
90
- - **浏览器选择** - 自动检测已安装浏览器,支持无痕模式
91
- - **文档中心** - 内置帮助文档,左侧目录 + 右侧 Markdown 渲染
92
-
93
- ### v1.4.0 功能
94
- - **Token 预刷新** - 后台每 5 分钟检查,提前 15 分钟自动刷新
95
- - **健康检查** - 每 10 分钟检测账号可用性,自动标记状态
96
- - **请求统计增强** - 按账号/模型统计,24 小时趋势
97
- - **请求重试机制** - 网络错误/5xx 自动重试,指数退避
98
-
99
- ## 工具调用支持
100
-
101
- | 功能 | Anthropic (Claude Code) | OpenAI (Codex CLI) | Gemini |
102
- |------|------------------------|-------------------|--------|
103
- | 工具定义 | ✅ `tools` | ✅ `tools.function` | ✅ `functionDeclarations` |
104
- | 工具调用响应 | ✅ `tool_use` | ✅ `tool_calls` | ✅ `functionCall` |
105
- | 工具结果 | ✅ `tool_result` | �� `tool` 角色消息 | ✅ `functionResponse` |
106
- | 强制工具调用 | ✅ `tool_choice` | ✅ `tool_choice` | ✅ `toolConfig.mode` |
107
- | 工具数量限制 | ✅ 50 个 | ✅ 50 个 | ✅ 50 个 |
108
- | 历史消息修复 | ✅ | ✅ | ✅ |
109
- | 图片理解 | ✅ | ✅ | ❌ |
110
- | 网络搜索 | ✅ | ✅ | ❌ |
111
-
112
- ## 已知限制
113
-
114
- ### 对话长度限制
115
-
116
- Kiro API 有输入长度限制。当对话历史过长时,会返回错误:
117
-
118
- ```
119
- Input is too long. (CONTENT_LENGTH_EXCEEDS_THRESHOLD)
120
- ```
121
-
122
- #### 自动处理(v1.6.0+)
123
-
124
- 代理内置了历史消息管理功能,可在「设置」页面配置:
125
-
126
- - **错误重试**(默认):遇到长度错误时自动截断并重试
127
- - **智能摘要**:用 AI 生成早期对话摘要,保留关键信息
128
- - **摘要缓存**(默认):历史变化不大时复用最近摘要,减少重复 LLM 调用
129
- - **自动截断**:每次请求前优先保留最新上下文并摘要前文,必要时按数量/字符数截断
130
- - **预估检测**:预估 token 数量,超限预先截断
131
-
132
- 如需 **关闭自动压缩/重试**(超限时直接报错),可设置环境变量 `KIROPROXY_HISTORY_ERROR_RETRY=0`,或将历史配置的 `strategies` 中移除 `error_retry`。
133
-
134
- 摘要缓存可通过以下配置项调整(默认值):
135
- - `summary_cache_enabled`: `true`
136
- - `summary_cache_min_delta_messages`: `3`
137
- - `summary_cache_min_delta_chars`: `4000`
138
- - `summary_cache_max_age_seconds`: `180`
139
-
140
- #### 手动处理
141
-
142
- 1. 在 Claude Code 中输入 `/clear` 清空对话历史
143
- 2. 告诉 AI 你之前在做什么,它会读取代码文件恢复上下文
144
-
145
- ## 快速开始
146
-
147
- ### 方式一:下载预编译版本
148
-
149
- 从 [Releases](../../releases) 下载对应平台的安装包,解压后直接运行。
150
-
151
- ### 方式二:从源码运行
152
-
153
- ```bash
154
- # 克隆项目
155
- git clone https://github.com/yourname/kiro-proxy.git
156
- cd kiro-proxy
157
-
158
- # 创建虚拟环境
159
- python -m venv venv
160
- source venv/bin/activate # Windows: venv\Scripts\activate
161
-
162
- # 安装依赖
163
- pip install -r requirements.txt
164
-
165
- # 运行
166
- python run.py
167
-
168
- # 或指定端口
169
- python run.py 8081
170
- ```
171
-
172
- 启动后访问 http://localhost:8080
173
-
174
- ### 命令行工具 (CLI)
175
-
176
- 无 GUI 服务器可使用 CLI 管理账号:
177
-
178
- ```bash
179
- # 账号管理
180
- python run.py accounts list # 列出账号
181
- python run.py accounts export -o acc.json # 导出账号
182
- python run.py accounts import acc.json # 导入账号
183
- python run.py accounts add # 交互式添加 Token
184
- python run.py accounts scan --auto # 扫描并自动添加本地 Token
185
-
186
- # 登录
187
- python run.py login google # Google 登录
188
- python run.py login github # GitHub 登录
189
- python run.py login remote --host myserver.com:8080 # 生成远程登录链接
190
-
191
- # 服务
192
- python run.py serve # 启动服务 (默认 8080)
193
- python run.py serve -p 8081 # 指定端口
194
- python run.py status # 查看状态
195
- ```
196
-
197
- ### 登录获取 Token
198
-
199
- **方式一:在线登录(推荐)**
200
- 1. 打开 Web UI,点击「在线登录」
201
- 2. 选择登录方式:Google / GitHub / AWS Builder ID
202
- 3. 在浏览器中完成授权
203
- 4. 账号自动添加
204
-
205
- **方式二:扫描 Token**
206
- 1. 打开 Kiro IDE,使用 Google/GitHub 账号登录
207
- 2. 登录成功后 token 自动保存到 `~/.aws/sso/cache/`
208
- 3. 在 Web UI 点击「扫描 Token」添加账号
209
-
210
- ## CLI 配置
211
-
212
- ### 模型对照表
213
-
214
- | Kiro 模型 | 能力 | Claude Code | Codex |
215
- |-----------|------|-------------|-------|
216
- | `claude-sonnet-4` | ⭐⭐⭐ 推荐 | `claude-sonnet-4` | `gpt-4o` |
217
- | `claude-sonnet-4.5` | ⭐⭐⭐⭐ 更强 | `claude-sonnet-4.5` | `gpt-4o` |
218
- | `claude-haiku-4.5` | ⚡ 快速 | `claude-haiku-4.5` | `gpt-4o-mini` |
219
-
220
- ### Claude Code 配置
221
-
222
- ```
223
- 名称: Kiro Proxy
224
- API Key: any
225
- Base URL: http://localhost:8080
226
- 模型: claude-sonnet-4
227
- ```
228
-
229
- ### Codex 配置
230
-
231
- Codex CLI 使用 OpenAI Responses API,配置如下:
232
-
233
- ```bash
234
- # 设置环境变量
235
- export OPENAI_API_KEY=any
236
- export OPENAI_BASE_URL=http://localhost:8080/v1
237
-
238
- # 运行 Codex
239
- codex
240
- ```
241
-
242
- 或在 `~/.codex/config.toml` 中配置:
243
-
244
- ```toml
245
- [providers.openai]
246
- api_key = "any"
247
- base_url = "http://localhost:8080/v1"
248
- ```
249
-
250
- ## 思考功能支持
251
-
252
- ### 什么是思考功能
253
-
254
- 思考功能(Extended Thinking)允许 Claude 在生成回答前展示其思考过程,帮助用户理解 AI 的推理步骤。
255
-
256
- ### 如何使用
257
-
258
- 在请求中添加 `thinking`(或对应协议的 thinking 配置)即可启用:
259
-
260
- ```json
261
- {
262
- "model": "claude-sonnet-4.5",
263
- "messages": [
264
- {
265
- "role": "user",
266
- "content": "解释一下量子计算的原理"
267
- }
268
- ],
269
- "thinking": {
270
- "thinking_type": "enabled",
271
- "budget_tokens": 20000
272
- },
273
- "stream": true
274
- }
275
- ```
276
-
277
- OpenAI Chat Completions (`POST /v1/chat/completions`) 也支持:
278
-
279
- ```json
280
- {
281
- "model": "gpt-4o",
282
- "messages": [{"role": "user", "content": "解释一下量子计算的原理"}],
283
- "thinking": { "type": "enabled" },
284
- "stream": true
285
- }
286
- ```
287
-
288
- OpenAI Responses (`POST /v1/responses`) 也支持:
289
-
290
- ```json
291
- {
292
- "model": "gpt-4o",
293
- "input": "解释一下量子计算的原理",
294
- "thinking": { "type": "enabled" }
295
- }
296
- ```
297
-
298
- Gemini generateContent (`POST /v1/models/{model}:generateContent`) 也支持:
299
-
300
- ```json
301
- {
302
- "contents": [{"role": "user", "parts": [{"text": "解释一下量子计算的原理"}]}],
303
- "generationConfig": {
304
- "thinkingConfig": { "includeThoughts": true }
305
- }
306
- }
307
- ```
308
-
309
- ### 参数说明
310
-
311
- - `thinking_type`: 思考类型,设为 `"enabled"` 启用思考功能
312
- - `budget_tokens`: 思考过程的 token 预算(不传则视为无限制)
313
-
314
- ### 响应格式
315
-
316
- 启用思考功能后,流式响应会包含两种内容块:
317
-
318
- 1. **思考块**(type: "thinking"):展示 AI 的思考过程
319
- 2. **文本块**(type: "text"):最终的回答内容
320
-
321
- 示例响应:
322
- ```
323
- data: {"type":"content_block_start","index":1,"content_block":{"type":"thinking","thinking":""}}
324
- data: {"type":"content_block_delta","index":1,"delta":{"type":"thinking_delta","thinking":"让我思考一下量子计算的原理..."}}
325
- data: {"type":"content_block_stop","index":1}
326
- data: {"type":"content_block_start","index":0,"content_block":{"type":"text","text":""}}
327
- data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"量子计算是一种..."}}
328
- data: {"type":"content_block_stop","index":0}
329
- ```
330
-
331
- ## API 端点
332
-
333
- | 协议 | 端点 | 用途 |
334
- |------|------|------|
335
- | OpenAI | `POST /v1/chat/completions` | Chat Completions API |
336
- | OpenAI | `POST /v1/responses` | Responses API (Codex CLI) |
337
- | OpenAI | `GET /v1/models` | 模型列表 |
338
- | Anthropic | `POST /v1/messages` | Claude Code |
339
- | Anthropic | `POST /v1/messages/count_tokens` | Token 计数 |
340
- | Gemini | `POST /v1/models/{model}:generateContent` | Gemini CLI |
341
-
342
- ### 管理 API
343
-
344
- | 端点 | 方法 | 说明 |
345
- |------|------|------|
346
- | `/api/accounts` | GET | 获取所有账号状态 |
347
- | `/api/accounts/{id}` | GET | 获取账号详情 |
348
- | `/api/accounts/{id}/usage` | GET | 获取账号用量信息 |
349
- | `/api/accounts/{id}/refresh` | POST | 刷新账号 Token |
350
- | `/api/accounts/{id}/restore` | POST | 恢复账号(从冷却状态) |
351
- | `/api/accounts/refresh-all` | POST | 刷新所有即将过期的 Token |
352
- | `/api/flows` | GET | 获取流量记录 |
353
- | `/api/flows/stats` | GET | 获取流量统计 |
354
- | `/api/flows/{id}` | GET | 获取流量详情 |
355
- | `/api/quota` | GET | 获取配额状态 |
356
- | `/api/stats` | GET | 获取统计信息 |
357
- | `/api/health-check` | POST | 手动触发健康检查 |
358
- | `/api/browsers` | GET | 获取可用浏览器列表 |
359
- | `/api/docs` | GET | 获取文档列表 |
360
- | `/api/docs/{id}` | GET | 获取文档内容 |
361
-
362
- ## 项目结构
363
-
364
- ```
365
- kiro_proxy/
366
- ├── main.py # FastAPI 应用入口
367
- ├── config.py # 全局配置
368
- ├── converters.py # 协议转换
369
-
370
- ├── core/ # 核心模块
371
- │ ├── account.py # 账号管理
372
- │ ├── state.py # 全局状态
373
- │ ├── persistence.py # 配置持久化
374
- │ ├── scheduler.py # 后台任务调度
375
- │ ├── stats.py # 请求统计
376
- │ ├── retry.py # 重试机制
377
- │ ├── browser.py # 浏览器检测
378
- │ ├── flow_monitor.py # 流量监控
379
- │ └── usage.py # 用量查询
380
-
381
- ├── credential/ # 凭证管理
382
- │ ├── types.py # KiroCredentials
383
- │ ├── fingerprint.py # Machine ID 生成
384
- │ ├── quota.py # 配额管理器
385
- │ └── refresher.py # Token 刷新
386
-
387
- ├── auth/ # 认证模块
388
- │ └── device_flow.py # Device Code Flow / Social Auth
389
-
390
- ├── handlers/ # API 处理器
391
- │ ├── anthropic.py # /v1/messages
392
- │ ├── openai.py # /v1/chat/completions
393
- │ ├── responses.py # /v1/responses (Codex CLI)
394
- │ ├── gemini.py # /v1/models/{model}:generateContent
395
- │ └── admin.py # 管理 API
396
-
397
- ├── cli.py # 命令行工具
398
-
399
- ├── docs/ # 内置文档
400
- │ ├── 01-quickstart.md # 快速开始
401
- │ ├── 02-features.md # 功能特性
402
- │ ├── 03-faq.md # 常见问题
403
- │ └── 04-api.md # API 参考
404
-
405
- └── web/
406
- └── html.py # Web UI (组件化单文件)
407
- ```
408
-
409
- ## 构建
410
-
411
- ```bash
412
- # 安装构建依赖
413
- pip install pyinstaller
414
-
415
- # 构建
416
- python build.py
417
- ```
418
-
419
- 输出文件在 `dist/` 目录。
420
-
421
- ## 免责声明
422
-
423
- 本项目仅供学习研究,禁止商用。使用本项目产生的任何后果由使用者自行承担,与作者无关。
424
-
425
- 本项目与 Kiro / AWS / Anthropic 官方无关。
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
KiroProxy/kiro_proxy/main_legacy.py DELETED
@@ -1,694 +0,0 @@
1
- """Kiro API Proxy - 主应用"""
2
- import json
3
- import uuid
4
- import httpx
5
- import sys
6
- import time
7
- from pathlib import Path
8
- from contextlib import asynccontextmanager
9
- from fastapi import FastAPI, Request, HTTPException
10
- from fastapi.responses import HTMLResponse, StreamingResponse, JSONResponse
11
- from fastapi.middleware.cors import CORSMiddleware
12
-
13
- from . import __version__
14
- from .config import MODELS_URL
15
- from .core import state, scheduler, stats_manager, get_quota_scheduler, get_refresh_manager
16
- from .handlers import anthropic, openai, gemini, admin
17
- from .handlers import responses as responses_handler
18
- from .web.html import HTML_PAGE
19
- from .credential import generate_machine_id, get_kiro_version
20
-
21
-
22
- def get_resource_path(relative_path: str) -> Path:
23
- """获取资源文件路径,支持从打包资源读取"""
24
- base_path = Path(sys._MEIPASS) if hasattr(sys, '_MEIPASS') else Path(__file__).parent.parent
25
- return base_path / relative_path
26
-
27
-
28
- @asynccontextmanager
29
- async def lifespan(app: FastAPI):
30
- """应用生命周期管理"""
31
- # 启动时
32
- await scheduler.start()
33
-
34
- # 初始化刷新管理器
35
- refresh_manager = get_refresh_manager()
36
- refresh_manager.set_accounts_getter(lambda: state.accounts)
37
-
38
- # 启动时先刷新所有需要刷新的 Token,避免首次请求使用过期 Token
39
- accounts = state.accounts
40
- if accounts:
41
- print(f"[Startup] 检查 {len(accounts)} 个账号的 Token 状态...")
42
- for account in accounts:
43
- if account.enabled and refresh_manager.should_refresh_token(account):
44
- try:
45
- success, msg = await refresh_manager.refresh_token_if_needed(account)
46
- if success:
47
- print(f"[Startup] 账号 {account.name} Token 刷新成功")
48
- else:
49
- print(f"[Startup] 账号 {account.name} Token 刷新失败: {msg}")
50
- except Exception as e:
51
- print(f"[Startup] 账号 {account.name} Token 刷新异常: {e}")
52
-
53
- # 启动额度调度器(在 Token 刷新之后,避免首次额度获取使用过期 Token)
54
- quota_scheduler = get_quota_scheduler()
55
- quota_scheduler.set_accounts_getter(lambda: state.accounts)
56
- await quota_scheduler.start()
57
-
58
- await refresh_manager.start_auto_refresh()
59
-
60
- yield
61
-
62
- # 关闭时
63
- await refresh_manager.stop_auto_refresh()
64
- await quota_scheduler.stop()
65
- await scheduler.stop()
66
-
67
-
68
- app = FastAPI(title="Kiro API Proxy", docs_url="/docs", redoc_url=None, lifespan=lifespan)
69
-
70
- app.add_middleware(
71
- CORSMiddleware,
72
- allow_origins=["*"],
73
- allow_credentials=True,
74
- allow_methods=["*"],
75
- allow_headers=["*"],
76
- )
77
-
78
-
79
- @app.middleware("http")
80
- async def log_requests(request: Request, call_next):
81
- start_time = time.time()
82
- path = request.url.path
83
- method = request.method
84
-
85
- body_str = "<skipped>"
86
-
87
- print(f"[Request] {method} {path} | Body: {body_str}")
88
-
89
- response = await call_next(request)
90
-
91
- duration = (time.time() - start_time) * 1000
92
- print(f"[Response] {method} {path} - {response.status_code} ({duration:.2f}ms)")
93
- return response
94
-
95
-
96
- # ==================== Web UI ====================
97
-
98
- @app.get("/", response_class=HTMLResponse)
99
- async def index():
100
- return HTML_PAGE
101
-
102
-
103
- @app.get("/assets/{path:path}")
104
- async def serve_assets(path: str):
105
- """提供静态资源"""
106
- file_path = get_resource_path("assets") / path
107
- if file_path.exists():
108
- content_type = "image/svg+xml" if path.endswith(".svg") else "application/octet-stream"
109
- return StreamingResponse(open(file_path, "rb"), media_type=content_type)
110
- raise HTTPException(status_code=404)
111
-
112
-
113
- # ==================== API 端点 ====================
114
-
115
- @app.get("/v1/models")
116
- async def models():
117
- """获取可用模型列表"""
118
- try:
119
- account = state.get_available_account()
120
- if not account:
121
- raise Exception("No available account")
122
-
123
- token = account.get_token()
124
- machine_id = account.get_machine_id()
125
- kiro_version = get_kiro_version()
126
-
127
- headers = {
128
- "content-type": "application/json",
129
- "x-amz-user-agent": f"aws-sdk-js/1.0.0 KiroIDE-{kiro_version}-{machine_id}",
130
- "amz-sdk-invocation-id": str(uuid.uuid4()),
131
- "Authorization": f"Bearer {token}",
132
- }
133
- async with httpx.AsyncClient(verify=False, timeout=30) as client:
134
- resp = await client.get(MODELS_URL, headers=headers, params={"origin": "AI_EDITOR"})
135
- if resp.status_code == 200:
136
- data = resp.json()
137
- return {
138
- "object": "list",
139
- "data": [
140
- {
141
- "id": m["modelId"],
142
- "object": "model",
143
- "owned_by": "kiro",
144
- "name": m["modelName"],
145
- }
146
- for m in data.get("models", [])
147
- ]
148
- }
149
- except Exception:
150
- pass
151
-
152
- # 降级返回静态列表
153
- return {"object": "list", "data": [
154
- {"id": "auto", "object": "model", "owned_by": "kiro", "name": "Auto"},
155
- {"id": "claude-sonnet-4.5", "object": "model", "owned_by": "kiro", "name": "Claude Sonnet 4.5"},
156
- {"id": "claude-sonnet-4", "object": "model", "owned_by": "kiro", "name": "Claude Sonnet 4"},
157
- {"id": "claude-haiku-4.5", "object": "model", "owned_by": "kiro", "name": "Claude Haiku 4.5"},
158
- ]}
159
-
160
-
161
- # Anthropic 协议
162
- @app.post("/v1/messages")
163
- async def anthropic_messages(request: Request):
164
- print(f"[Main] Received /v1/messages request from {request.client.host}")
165
- return await anthropic.handle_messages(request)
166
-
167
- @app.post("/v1/messages/count_tokens")
168
- async def anthropic_count_tokens(request: Request):
169
- return await anthropic.handle_count_tokens(request)
170
-
171
-
172
-
173
- @app.post("/v1/complete")
174
- async def anthropic_complete(request: Request):
175
- print(f"[Main] Received /v1/complete request from {request.client.host}")
176
- # 暂时重定向或提示,或者如果需要可以实现 handle_complete
177
- return JSONResponse(
178
- status_code=400,
179
- content={"error": {"type": "invalid_request_error", "message": "KiroProxy currently only supports /v1/messages. Please check if your client can be configured to use Messages API."}}
180
- )
181
-
182
-
183
- # OpenAI 协议
184
- @app.post("/v1/chat/completions")
185
- async def openai_chat(request: Request):
186
- return await openai.handle_chat_completions(request)
187
-
188
-
189
- # OpenAI Responses API (Codex CLI 新版本)
190
- @app.post("/v1/responses")
191
- async def openai_responses(request: Request):
192
- return await responses_handler.handle_responses(request)
193
-
194
-
195
- # Gemini 协议
196
- @app.post("/v1beta/models/{model_name}:generateContent")
197
- @app.post("/v1/models/{model_name}:generateContent")
198
- async def gemini_generate(model_name: str, request: Request):
199
- return await gemini.handle_generate_content(model_name, request)
200
-
201
-
202
- # ==================== 管理 API ====================
203
-
204
- @app.get("/api/status")
205
- async def api_status():
206
- return await admin.get_status()
207
-
208
- @app.post("/api/event_logging/batch")
209
- async def api_event_logging_batch(request: Request):
210
- return await admin.event_logging_batch(request)
211
-
212
-
213
- @app.get("/api/stats")
214
- async def api_stats():
215
- return await admin.get_stats()
216
-
217
-
218
- @app.get("/api/logs")
219
- async def api_logs(limit: int = 100):
220
- return await admin.get_logs(limit)
221
-
222
-
223
- # ==================== 账号导入导出 API ====================
224
-
225
- @app.get("/api/accounts/export")
226
- async def api_export_accounts():
227
- """导出所有账号配置"""
228
- return await admin.export_accounts()
229
-
230
-
231
- @app.post("/api/accounts/import")
232
- async def api_import_accounts(request: Request):
233
- """导入账号配置"""
234
- return await admin.import_accounts(request)
235
-
236
-
237
- @app.post("/api/accounts/manual")
238
- async def api_add_manual_token(request: Request):
239
- """手动添加 Token"""
240
- return await admin.add_manual_token(request)
241
-
242
-
243
- @app.post("/api/accounts/batch")
244
- async def api_batch_import_accounts(request: Request):
245
- """批量导入账号"""
246
- return await admin.batch_import_accounts(request)
247
-
248
-
249
- @app.post("/api/accounts/refresh-all")
250
- async def api_refresh_all():
251
- """刷新所有即将过期的 token"""
252
- return await admin.refresh_all_tokens()
253
-
254
-
255
- # ==================== 额度管理 API (必须在 {account_id} 路由之前) ====================
256
-
257
- @app.get("/api/accounts/status")
258
- async def api_accounts_status_enhanced():
259
- """获取完整账号状态(增强版)"""
260
- return await admin.get_accounts_status_enhanced()
261
-
262
-
263
- @app.get("/api/accounts/summary")
264
- async def api_accounts_summary():
265
- """获取账号汇总统计"""
266
- return await admin.get_accounts_summary()
267
-
268
-
269
- @app.post("/api/accounts/refresh-all-quotas")
270
- async def api_refresh_all_quotas():
271
- """刷新所有账号额度"""
272
- return await admin.refresh_all_quotas()
273
-
274
-
275
- # ==================== 刷新进度 API ====================
276
-
277
- @app.get("/api/refresh/progress")
278
- async def api_refresh_progress():
279
- """获取刷新进度"""
280
- return await admin.get_refresh_progress()
281
-
282
-
283
- @app.post("/api/refresh/all")
284
- async def api_refresh_all_with_progress():
285
- """批量刷新(带进度和锁检查)"""
286
- return await admin.refresh_all_with_progress()
287
-
288
-
289
- @app.get("/api/refresh/config")
290
- async def api_get_refresh_config():
291
- """获取刷新配置"""
292
- return await admin.get_refresh_config()
293
-
294
-
295
- @app.put("/api/refresh/config")
296
- async def api_update_refresh_config(request: Request):
297
- """更新刷新配置"""
298
- return await admin.update_refresh_config(request)
299
-
300
-
301
- @app.get("/api/refresh/status")
302
- async def api_refresh_status():
303
- """获取刷新管理器状态"""
304
- return await admin.get_refresh_manager_status()
305
-
306
-
307
- @app.get("/api/accounts")
308
- async def api_accounts():
309
- return await admin.get_accounts()
310
-
311
-
312
- @app.post("/api/accounts")
313
- async def api_add_account(request: Request):
314
- return await admin.add_account(request)
315
-
316
-
317
- @app.delete("/api/accounts/{account_id}")
318
- async def api_delete_account(account_id: str):
319
- return await admin.delete_account(account_id)
320
-
321
-
322
- @app.put("/api/accounts/{account_id}")
323
- async def api_update_account(account_id: str, request: Request):
324
- return await admin.update_account(account_id, request)
325
-
326
-
327
- @app.post("/api/accounts/{account_id}/toggle")
328
- async def api_toggle_account(account_id: str):
329
- return await admin.toggle_account(account_id)
330
-
331
-
332
- @app.post("/api/speedtest")
333
- async def api_speedtest():
334
- return await admin.speedtest()
335
-
336
-
337
- @app.get("/api/accounts/{account_id}/test")
338
- async def api_test_account_token(account_id: str):
339
- """测试指定账号的 Token 是否有效"""
340
- return await admin.test_account_token(account_id)
341
-
342
-
343
- @app.get("/api/token/scan")
344
- async def api_scan_tokens():
345
- return await admin.scan_tokens()
346
-
347
-
348
- @app.post("/api/token/add-from-scan")
349
- async def api_add_from_scan(request: Request):
350
- return await admin.add_from_scan(request)
351
-
352
-
353
- @app.get("/api/config/export")
354
- async def api_export_config():
355
- return await admin.export_config()
356
-
357
-
358
- @app.post("/api/config/import")
359
- async def api_import_config(request: Request):
360
- return await admin.import_config(request)
361
-
362
-
363
- @app.post("/api/token/refresh-check")
364
- async def api_refresh_check():
365
- return await admin.refresh_token_check()
366
-
367
-
368
- @app.post("/api/accounts/{account_id}/refresh")
369
- async def api_refresh_account(account_id: str):
370
- """刷新指定账号的 token(集成 RefreshManager)"""
371
- return await admin.refresh_account_token_with_manager(account_id)
372
-
373
-
374
- @app.post("/api/accounts/{account_id}/restore")
375
- async def api_restore_account(account_id: str):
376
- """恢复账号(从冷却状态)"""
377
- return await admin.restore_account(account_id)
378
-
379
-
380
- @app.get("/api/accounts/{account_id}/usage")
381
- async def api_account_usage(account_id: str):
382
- """获取账号用量信息"""
383
- return await admin.get_account_usage_info(account_id)
384
-
385
-
386
- @app.get("/api/accounts/{account_id}")
387
- async def api_account_detail(account_id: str):
388
- """获取账号详细信息"""
389
- return await admin.get_account_detail(account_id)
390
-
391
-
392
- @app.post("/api/accounts/{account_id}/refresh-quota")
393
- async def api_refresh_account_quota(account_id: str):
394
- """刷新单个账号额度(先刷新 Token)"""
395
- return await admin.refresh_account_quota_with_token(account_id)
396
-
397
-
398
- # ==================== 优先账号 API ====================
399
-
400
- @app.get("/api/priority")
401
- async def api_get_priority_accounts():
402
- """获取优先账号列表"""
403
- return await admin.get_priority_accounts()
404
-
405
-
406
- @app.post("/api/priority/{account_id}")
407
- async def api_set_priority_account(account_id: str, request: Request):
408
- """设置优先账号"""
409
- return await admin.set_priority_account(account_id, request)
410
-
411
-
412
- @app.delete("/api/priority/{account_id}")
413
- async def api_remove_priority_account(account_id: str):
414
- """取消优先账号"""
415
- return await admin.remove_priority_account(account_id)
416
-
417
-
418
- @app.put("/api/priority/reorder")
419
- async def api_reorder_priority_accounts(request: Request):
420
- """调整优先账号顺序"""
421
- return await admin.reorder_priority_accounts(request)
422
-
423
-
424
- @app.get("/api/quota")
425
- async def api_quota_status():
426
- """获取配额状态"""
427
- return await admin.get_quota_status()
428
-
429
-
430
- @app.get("/api/kiro/login-url")
431
- async def api_login_url():
432
- return await admin.get_kiro_login_url()
433
-
434
-
435
- @app.get("/api/stats/detailed")
436
- async def api_detailed_stats():
437
- """获取详细统计信息"""
438
- return await admin.get_detailed_stats()
439
-
440
-
441
- @app.post("/api/health-check")
442
- async def api_health_check():
443
- """手动触发健康检查"""
444
- return await admin.run_health_check()
445
-
446
-
447
- @app.get("/api/browsers")
448
- async def api_browsers():
449
- """获取可用浏览器列表"""
450
- return await admin.get_browsers()
451
-
452
-
453
- # ==================== Kiro 登录 API ====================
454
-
455
- @app.post("/api/kiro/login/start")
456
- async def api_kiro_login_start(request: Request):
457
- """启动 Kiro 设备授权登录"""
458
- return await admin.start_kiro_login(request)
459
-
460
-
461
- @app.get("/api/kiro/login/poll")
462
- async def api_kiro_login_poll():
463
- """轮询登录状态"""
464
- return await admin.poll_kiro_login()
465
-
466
-
467
- @app.post("/api/kiro/login/cancel")
468
- async def api_kiro_login_cancel():
469
- """取消登录"""
470
- return await admin.cancel_kiro_login()
471
-
472
-
473
- @app.get("/api/kiro/login/status")
474
- async def api_kiro_login_status():
475
- """获取登录状态"""
476
- return await admin.get_kiro_login_status()
477
-
478
-
479
- # ==================== Social Auth API (Google/GitHub) ====================
480
-
481
- @app.post("/api/kiro/social/start")
482
- async def api_social_login_start(request: Request):
483
- """启动 Social Auth 登录"""
484
- return await admin.start_social_login(request)
485
-
486
-
487
- @app.post("/api/kiro/social/exchange")
488
- async def api_social_token_exchange(request: Request):
489
- """交换 Social Auth Token"""
490
- return await admin.exchange_social_token(request)
491
-
492
-
493
- @app.post("/api/kiro/social/cancel")
494
- async def api_social_login_cancel():
495
- """取消 Social Auth 登录"""
496
- return await admin.cancel_social_login()
497
-
498
-
499
- @app.get("/api/kiro/social/status")
500
- async def api_social_login_status():
501
- """获取 Social Auth 状态"""
502
- return await admin.get_social_login_status()
503
-
504
-
505
- # ==================== 协议注册 API ====================
506
-
507
- @app.post("/api/protocol/register")
508
- async def api_register_protocol():
509
- """注册 kiro:// 协议"""
510
- return await admin.register_kiro_protocol()
511
-
512
-
513
- @app.post("/api/protocol/unregister")
514
- async def api_unregister_protocol():
515
- """取消注册 kiro:// 协议"""
516
- return await admin.unregister_kiro_protocol()
517
-
518
-
519
- @app.get("/api/protocol/status")
520
- async def api_protocol_status():
521
- """获取协议注册状态"""
522
- return await admin.get_protocol_status()
523
-
524
-
525
- @app.get("/api/protocol/callback")
526
- async def api_protocol_callback():
527
- """获取回调结果"""
528
- return await admin.get_callback_result()
529
-
530
-
531
- # ==================== Flow Monitor API ====================
532
-
533
- @app.get("/api/flows")
534
- async def api_flows(
535
- protocol: str = None,
536
- model: str = None,
537
- account_id: str = None,
538
- state: str = None,
539
- has_error: bool = None,
540
- bookmarked: bool = None,
541
- search: str = None,
542
- limit: int = 50,
543
- offset: int = 0,
544
- ):
545
- """查询 Flows"""
546
- return await admin.get_flows(
547
- protocol=protocol,
548
- model=model,
549
- account_id=account_id,
550
- state_filter=state,
551
- has_error=has_error,
552
- bookmarked=bookmarked,
553
- search=search,
554
- limit=limit,
555
- offset=offset,
556
- )
557
-
558
-
559
- @app.get("/api/flows/stats")
560
- async def api_flow_stats():
561
- """获取 Flow 统计"""
562
- return await admin.get_flow_stats()
563
-
564
-
565
- @app.get("/api/flows/{flow_id}")
566
- async def api_flow_detail(flow_id: str):
567
- """获取 Flow 详情"""
568
- return await admin.get_flow_detail(flow_id)
569
-
570
-
571
- @app.post("/api/flows/{flow_id}/bookmark")
572
- async def api_bookmark_flow(flow_id: str, request: Request):
573
- """书签 Flow"""
574
- return await admin.bookmark_flow(flow_id, request)
575
-
576
-
577
- @app.post("/api/flows/{flow_id}/note")
578
- async def api_add_flow_note(flow_id: str, request: Request):
579
- """添加 Flow 备注"""
580
- return await admin.add_flow_note(flow_id, request)
581
-
582
-
583
- @app.post("/api/flows/{flow_id}/tag")
584
- async def api_add_flow_tag(flow_id: str, request: Request):
585
- """添加 Flow 标签"""
586
- return await admin.add_flow_tag(flow_id, request)
587
-
588
-
589
- @app.post("/api/flows/export")
590
- async def api_export_flows(request: Request):
591
- """导出 Flows"""
592
- return await admin.export_flows(request)
593
-
594
-
595
- # ==================== 历史消息管理 API ====================
596
-
597
- from .core import get_history_config, update_history_config, TruncateStrategy
598
- from .core.rate_limiter import get_rate_limiter
599
-
600
- @app.get("/api/settings/history")
601
- async def api_get_history_config():
602
- """获取历史消息管理配置"""
603
- config = get_history_config()
604
- return config.to_dict()
605
-
606
-
607
- @app.post("/api/settings/history")
608
- async def api_update_history_config(request: Request):
609
- """更新历史消息管理配置"""
610
- data = await request.json()
611
- update_history_config(data)
612
- return {"ok": True, "config": get_history_config().to_dict()}
613
-
614
-
615
- # ==================== 限速配置 API ====================
616
-
617
- @app.get("/api/settings/rate-limit")
618
- async def api_get_rate_limit_config():
619
- """获取限速配置"""
620
- limiter = get_rate_limiter()
621
- return {
622
- "enabled": limiter.config.enabled,
623
- "min_request_interval": limiter.config.min_request_interval,
624
- "max_requests_per_minute": limiter.config.max_requests_per_minute,
625
- "global_max_requests_per_minute": limiter.config.global_max_requests_per_minute,
626
- "stats": limiter.get_stats()
627
- }
628
-
629
-
630
- @app.post("/api/settings/rate-limit")
631
- async def api_update_rate_limit_config(request: Request):
632
- """更新限速配置"""
633
- data = await request.json()
634
- limiter = get_rate_limiter()
635
- limiter.update_config(**data)
636
- return {"ok": True, "config": {
637
- "enabled": limiter.config.enabled,
638
- "min_request_interval": limiter.config.min_request_interval,
639
- "max_requests_per_minute": limiter.config.max_requests_per_minute,
640
- "global_max_requests_per_minute": limiter.config.global_max_requests_per_minute,
641
- }}
642
-
643
-
644
- # ==================== 文档 API ====================
645
-
646
- # 文档标题映射
647
- DOC_TITLES = {
648
- "01-quickstart": "快速开始",
649
- "02-features": "功能特性",
650
- "03-faq": "常见问题",
651
- "04-api": "API 参考",
652
- "05-server-deploy": "服务器部署",
653
- }
654
-
655
- @app.get("/api/docs")
656
- async def api_docs_list():
657
- """获取文档列表"""
658
- docs_dir = get_resource_path("kiro_proxy/docs")
659
- docs = []
660
- if docs_dir.exists():
661
- for doc_file in sorted(docs_dir.glob("*.md")):
662
- doc_id = doc_file.stem
663
- title = DOC_TITLES.get(doc_id, doc_id)
664
- docs.append({"id": doc_id, "title": title})
665
- return {"docs": docs}
666
-
667
-
668
- @app.get("/api/docs/{doc_id}")
669
- async def api_docs_content(doc_id: str):
670
- """获取文档内容"""
671
- docs_dir = get_resource_path("kiro_proxy/docs")
672
- doc_file = docs_dir / f"{doc_id}.md"
673
- if not doc_file.exists():
674
- raise HTTPException(status_code=404, detail="文档不存在")
675
- content = doc_file.read_text(encoding="utf-8")
676
- title = DOC_TITLES.get(doc_id, doc_id)
677
- return {"id": doc_id, "title": title, "content": content}
678
-
679
-
680
- # ==================== 启动 ====================
681
-
682
- def run(port: int = 8080):
683
- import uvicorn
684
- print(f"\n{'='*50}")
685
- print(f" Kiro API Proxy v{__version__}")
686
- print(f" http://localhost:{port}")
687
- print(f"{'='*50}\n")
688
- uvicorn.run(app, host="0.0.0.0", port=port)
689
-
690
-
691
- if __name__ == "__main__":
692
- import sys
693
- port = int(sys.argv[1]) if len(sys.argv) > 1 else 8080
694
- run(port)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
KiroProxy/legacy/kiro_proxy.py DELETED
@@ -1,313 +0,0 @@
1
- #!/usr/bin/env python3
2
- """
3
- Kiro API 反向代理服务器
4
- 对外暴露 OpenAI 兼容接口,内部调用 Kiro/AWS Q API
5
- """
6
-
7
- import json
8
- import uuid
9
- import os
10
- import httpx
11
- from fastapi import FastAPI, Request, HTTPException
12
- from fastapi.responses import StreamingResponse, JSONResponse
13
- import uvicorn
14
- from datetime import datetime
15
- from pathlib import Path
16
- import logging
17
-
18
- logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
19
- logger = logging.getLogger(__name__)
20
-
21
- app = FastAPI(title="Kiro API Proxy")
22
-
23
- # Kiro API 配置
24
- KIRO_API_URL = "https://q.us-east-1.amazonaws.com/generateAssistantResponse"
25
- TOKEN_PATH = Path.home() / ".aws/sso/cache/kiro-auth-token.json"
26
- MACHINE_ID = "fa41d5def91e29225c73f6ea8ee0941a87bd812aae5239e3dde72c3ba7603a26"
27
-
28
- def get_kiro_token() -> str:
29
- """从本地文件读取 Kiro token"""
30
- try:
31
- with open(TOKEN_PATH) as f:
32
- data = json.load(f)
33
- return data.get("accessToken", "")
34
- except Exception as e:
35
- logger.error(f"读取 token 失败: {e}")
36
- raise HTTPException(status_code=500, detail="无法读取 Kiro token")
37
-
38
- def build_kiro_headers(token: str) -> dict:
39
- """构建 Kiro API 请求头"""
40
- return {
41
- "content-type": "application/json",
42
- "x-amzn-codewhisperer-optout": "true",
43
- "x-amzn-kiro-agent-mode": "vibe",
44
- "x-amz-user-agent": f"aws-sdk-js/1.0.27 KiroIDE-0.8.0-{MACHINE_ID}",
45
- "user-agent": f"aws-sdk-js/1.0.27 ua/2.1 os/linux lang/js md/nodejs api/codewhispererstreaming KiroIDE-0.8.0-{MACHINE_ID}",
46
- "amz-sdk-invocation-id": str(uuid.uuid4()),
47
- "amz-sdk-request": "attempt=1; max=3",
48
- "Authorization": f"Bearer {token}",
49
- }
50
-
51
- def build_kiro_request(messages: list, model: str, conversation_id: str = None) -> dict:
52
- """将 OpenAI 格式转换为 Kiro 格式"""
53
- if not conversation_id:
54
- conversation_id = str(uuid.uuid4())
55
-
56
- # 提取最后一条用户消息
57
- user_content = ""
58
- for msg in reversed(messages):
59
- if msg.get("role") == "user":
60
- user_content = msg.get("content", "")
61
- break
62
-
63
- return {
64
- "conversationState": {
65
- "conversationId": conversation_id,
66
- "currentMessage": {
67
- "userInputMessage": {
68
- "content": user_content,
69
- "modelId": model.replace("kiro-", ""), # 移除前缀
70
- "origin": "AI_EDITOR",
71
- "userInputMessageContext": {}
72
- }
73
- },
74
- "chatTriggerType": "MANUAL"
75
- }
76
- }
77
-
78
- def parse_kiro_response(response_data: dict) -> str:
79
- """解析 Kiro 响应,提取 AI 回复内容"""
80
- try:
81
- # Kiro 响应格式可能是流式的,需要解析
82
- if isinstance(response_data, dict):
83
- # 尝试多种可能的响应路径
84
- if "generateAssistantResponseResponse" in response_data:
85
- resp = response_data["generateAssistantResponseResponse"]
86
- if "assistantResponseEvent" in resp:
87
- event = resp["assistantResponseEvent"]
88
- if "content" in event:
89
- return event["content"]
90
-
91
- # 直接返回文本内容
92
- if "content" in response_data:
93
- return response_data["content"]
94
-
95
- if "message" in response_data:
96
- return response_data["message"]
97
-
98
- return json.dumps(response_data)
99
- except Exception as e:
100
- logger.error(f"解析响应失败: {e}")
101
- return str(response_data)
102
-
103
- def parse_event_stream(raw_content: bytes) -> str:
104
- """解析 AWS event-stream 格式的响应"""
105
- try:
106
- # 尝试直接解码为 UTF-8
107
- try:
108
- text = raw_content.decode('utf-8')
109
- # 如果是纯 JSON
110
- if text.startswith('{'):
111
- data = json.loads(text)
112
- return parse_kiro_response(data)
113
- except:
114
- pass
115
-
116
- # AWS event-stream 格式解析
117
- # 格式: [prelude (8 bytes)][headers][payload][message CRC (4 bytes)]
118
- content_parts = []
119
- pos = 0
120
-
121
- while pos < len(raw_content):
122
- if pos + 12 > len(raw_content):
123
- break
124
-
125
- # 读取 prelude: total_length (4 bytes) + headers_length (4 bytes) + prelude_crc (4 bytes)
126
- total_length = int.from_bytes(raw_content[pos:pos+4], 'big')
127
- headers_length = int.from_bytes(raw_content[pos+4:pos+8], 'big')
128
-
129
- if total_length == 0 or total_length > len(raw_content) - pos:
130
- break
131
-
132
- # 跳过 prelude (12 bytes) 和 headers
133
- payload_start = pos + 12 + headers_length
134
- payload_end = pos + total_length - 4 # 减去 message CRC
135
-
136
- if payload_start < payload_end:
137
- payload = raw_content[payload_start:payload_end]
138
- try:
139
- # 尝试解析 payload 为 JSON
140
- payload_text = payload.decode('utf-8')
141
- if payload_text.strip():
142
- payload_json = json.loads(payload_text)
143
-
144
- # 提取文本内容
145
- if "assistantResponseEvent" in payload_json:
146
- event = payload_json["assistantResponseEvent"]
147
- if "content" in event:
148
- content_parts.append(event["content"])
149
- elif "content" in payload_json:
150
- content_parts.append(payload_json["content"])
151
- elif "text" in payload_json:
152
- content_parts.append(payload_json["text"])
153
- else:
154
- logger.info(f" Event: {payload_text[:200]}")
155
- except Exception as e:
156
- logger.debug(f"解析 payload 失败: {e}")
157
-
158
- pos += total_length
159
-
160
- if content_parts:
161
- return "".join(content_parts)
162
-
163
- # 如果解析失败,返回原始内容的十六进制表示用于调试
164
- return f"[无法解析响应,原始数据: {raw_content[:500].hex()}]"
165
-
166
- except Exception as e:
167
- logger.error(f"解析 event-stream 失败: {e}")
168
- return f"[解析错误: {e}]"
169
-
170
- @app.get("/")
171
- async def root():
172
- """健康检查"""
173
- token_exists = TOKEN_PATH.exists()
174
- return {
175
- "status": "ok",
176
- "service": "Kiro API Proxy",
177
- "token_available": token_exists,
178
- "endpoints": {
179
- "chat": "/v1/chat/completions",
180
- "models": "/v1/models"
181
- }
182
- }
183
-
184
- @app.get("/v1/models")
185
- async def list_models():
186
- """列出可用模型 (OpenAI 兼容)"""
187
- return {
188
- "object": "list",
189
- "data": [
190
- {"id": "kiro-claude-sonnet-4", "object": "model", "owned_by": "kiro"},
191
- {"id": "kiro-claude-opus-4.5", "object": "model", "owned_by": "kiro"},
192
- {"id": "claude-sonnet-4", "object": "model", "owned_by": "kiro"},
193
- {"id": "claude-opus-4.5", "object": "model", "owned_by": "kiro"},
194
- ]
195
- }
196
-
197
- @app.post("/v1/chat/completions")
198
- async def chat_completions(request: Request):
199
- """OpenAI 兼容的聊天接口"""
200
- try:
201
- body = await request.json()
202
- except:
203
- raise HTTPException(status_code=400, detail="Invalid JSON")
204
-
205
- messages = body.get("messages", [])
206
- model = body.get("model", "claude-sonnet-4")
207
- stream = body.get("stream", False)
208
-
209
- if not messages:
210
- raise HTTPException(status_code=400, detail="messages is required")
211
-
212
- # 获取 token
213
- token = get_kiro_token()
214
-
215
- # 构建请求
216
- headers = build_kiro_headers(token)
217
- kiro_body = build_kiro_request(messages, model)
218
-
219
- logger.info(f"📤 发送请求到 Kiro API, model={model}")
220
- logger.info(f" 消息: {messages[-1].get('content', '')[:100]}...")
221
-
222
- try:
223
- async with httpx.AsyncClient(timeout=60.0, verify=False) as client:
224
- response = await client.post(
225
- KIRO_API_URL,
226
- headers=headers,
227
- json=kiro_body
228
- )
229
-
230
- logger.info(f"📥 Kiro 响应状态: {response.status_code}")
231
- logger.info(f" Content-Type: {response.headers.get('content-type')}")
232
-
233
- if response.status_code != 200:
234
- logger.error(f"Kiro API 错误: {response.text}")
235
- raise HTTPException(
236
- status_code=response.status_code,
237
- detail=f"Kiro API error: {response.text}"
238
- )
239
-
240
- # 处理响应 - 可能是 event-stream 或 JSON
241
- raw_content = response.content
242
- logger.info(f" 响应大小: {len(raw_content)} bytes")
243
- logger.info(f" 原始响应前200字节: {raw_content[:200]}")
244
-
245
- content = parse_event_stream(raw_content)
246
-
247
- logger.info(f" 回复: {content[:100]}...")
248
-
249
- # 返回 OpenAI 兼容格式
250
- return JSONResponse({
251
- "id": f"chatcmpl-{uuid.uuid4().hex[:8]}",
252
- "object": "chat.completion",
253
- "created": int(datetime.now().timestamp()),
254
- "model": model,
255
- "choices": [{
256
- "index": 0,
257
- "message": {
258
- "role": "assistant",
259
- "content": content
260
- },
261
- "finish_reason": "stop"
262
- }],
263
- "usage": {
264
- "prompt_tokens": 0,
265
- "completion_tokens": 0,
266
- "total_tokens": 0
267
- }
268
- })
269
-
270
- except httpx.RequestError as e:
271
- logger.error(f"请求失败: {e}")
272
- raise HTTPException(status_code=500, detail=str(e))
273
- except Exception as e:
274
- logger.error(f"未知错误: {e}")
275
- import traceback
276
- traceback.print_exc()
277
- raise HTTPException(status_code=500, detail=str(e))
278
-
279
- @app.get("/token/status")
280
- async def token_status():
281
- """检查 token 状态"""
282
- try:
283
- with open(TOKEN_PATH) as f:
284
- data = json.load(f)
285
- expires_at = data.get("expiresAt", "unknown")
286
- return {
287
- "valid": True,
288
- "expires_at": expires_at,
289
- "path": str(TOKEN_PATH)
290
- }
291
- except Exception as e:
292
- return {
293
- "valid": False,
294
- "error": str(e),
295
- "path": str(TOKEN_PATH)
296
- }
297
-
298
- if __name__ == "__main__":
299
- print("""
300
- ╔══════════════════════════════════════════════════════════════╗
301
- ║ Kiro API 反向代理服务器 ║
302
- ╠══════════════════════════════════════════════════════════════╣
303
- ║ 端口: 8000 ║
304
- ║ OpenAI 兼容接口: http://127.0.0.1:8000/v1/chat/completions ║
305
- ╠══════════════════════════════════════════════════════════════╣
306
- ║ 使用方法: ║
307
- ║ curl http://127.0.0.1:8000/v1/chat/completions \\ ║
308
- ║ -H "Content-Type: application/json" \\ ║
309
- ║ -d '{"model":"claude-sonnet-4","messages":[{"role":"user",║
310
- ║ "content":"Hello"}]}' ║
311
- ╚══════════════════════════════════════════════════════════════╝
312
- """)
313
- uvicorn.run(app, host="0.0.0.0", port=8000)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
KiroProxy/requirements.txt DELETED
@@ -1,13 +0,0 @@
1
- fastapi>=0.100.0
2
- uvicorn>=0.23.0
3
- httpx>=0.24.0
4
- requests>=2.31.0
5
- pytest>=7.0.0
6
- hypothesis>=6.0.0
7
- tiktoken>=0.5.0
8
- cbor2>=5.4.0
9
-
10
- # 数据库驱动(可选,根据DATABASE_URL自动选择)
11
- asyncpg>=0.28.0 # PostgreSQL
12
- aiomysql>=0.2.0 # MySQL
13
- aiosqlite>=0.19.0 # SQLite
 
 
 
 
 
 
 
 
 
 
 
 
 
 
KiroProxy/run.py DELETED
@@ -1,14 +0,0 @@
1
- #!/usr/bin/env python3
2
- """Kiro API Proxy 启动脚本"""
3
- import sys
4
-
5
- if __name__ == "__main__":
6
- # 如果有子命令参数,使用 CLI 模式
7
- if len(sys.argv) > 1 and sys.argv[1] in ("accounts", "login", "status", "serve"):
8
- from kiro_proxy.cli import main
9
- main()
10
- else:
11
- # 兼容旧的启动方式: python run.py [port]
12
- port = int(sys.argv[1]) if len(sys.argv) > 1 else 8080
13
- from kiro_proxy.main import run
14
- run(port)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
KiroProxy/scripts/capture_kiro.py DELETED
@@ -1,139 +0,0 @@
1
- #!/usr/bin/env python3
2
- """
3
- Kiro IDE 请求抓取工具
4
-
5
- 使用 mitmproxy 抓取 Kiro IDE 发送到 AWS 的请求。
6
-
7
- 安装:
8
- pip install mitmproxy
9
-
10
- 使用方法:
11
- 1. 运行此脚本: python capture_kiro.py
12
- 2. 设置系统代理为 127.0.0.1:8888
13
- 3. 安装 mitmproxy 的 CA 证书 (访问 http://mitm.it)
14
- 4. 启动 Kiro IDE 并使用
15
- 5. 查看 kiro_requests/ 目录下的抓取结果
16
-
17
- 或者使用 mitmproxy 命令行:
18
- mitmproxy --mode regular@8888 -s capture_kiro.py
19
-
20
- 或者使用 mitmdump (无 UI):
21
- mitmdump --mode regular@8888 -s capture_kiro.py
22
- """
23
-
24
- import json
25
- import os
26
- from datetime import datetime
27
- from mitmproxy import http, ctx
28
-
29
- # 创建输出目录
30
- OUTPUT_DIR = "kiro_requests"
31
- os.makedirs(OUTPUT_DIR, exist_ok=True)
32
-
33
- # 计数器
34
- request_count = 0
35
-
36
- def request(flow: http.HTTPFlow) -> None:
37
- """处理请求"""
38
- global request_count
39
-
40
- # 只抓取 Kiro/AWS 相关请求
41
- if "q.us-east-1.amazonaws.com" not in flow.request.host:
42
- return
43
-
44
- request_count += 1
45
- timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
46
-
47
- # 保存请求
48
- request_data = {
49
- "timestamp": timestamp,
50
- "method": flow.request.method,
51
- "url": flow.request.url,
52
- "headers": dict(flow.request.headers),
53
- "body": None
54
- }
55
-
56
- # 解析请求体
57
- if flow.request.content:
58
- try:
59
- request_data["body"] = json.loads(flow.request.content.decode('utf-8'))
60
- except:
61
- request_data["body_raw"] = flow.request.content.decode('utf-8', errors='replace')
62
-
63
- # 保存到文件
64
- filename = f"{OUTPUT_DIR}/{timestamp}_{request_count:04d}_request.json"
65
- with open(filename, 'w', encoding='utf-8') as f:
66
- json.dump(request_data, f, indent=2, ensure_ascii=False)
67
-
68
- ctx.log.info(f"[Kiro] Captured request #{request_count}: {flow.request.method} {flow.request.path}")
69
-
70
-
71
- def response(flow: http.HTTPFlow) -> None:
72
- """处理响应"""
73
- # 只处理 Kiro/AWS 相关响应
74
- if "q.us-east-1.amazonaws.com" not in flow.request.host:
75
- return
76
-
77
- timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
78
-
79
- # 保存响应
80
- response_data = {
81
- "timestamp": timestamp,
82
- "status_code": flow.response.status_code,
83
- "headers": dict(flow.response.headers),
84
- "body": None
85
- }
86
-
87
- # 响应体可能是 event-stream 格式
88
- if flow.response.content:
89
- try:
90
- # 尝试解析为 JSON
91
- response_data["body"] = json.loads(flow.response.content.decode('utf-8'))
92
- except:
93
- # 保存原始内容(可能是 event-stream)
94
- response_data["body_raw_length"] = len(flow.response.content)
95
- # 保存前 2000 字节用于调试
96
- response_data["body_preview"] = flow.response.content[:2000].decode('utf-8', errors='replace')
97
-
98
- # 保存到文件
99
- filename = f"{OUTPUT_DIR}/{timestamp}_{request_count:04d}_response.json"
100
- with open(filename, 'w', encoding='utf-8') as f:
101
- json.dump(response_data, f, indent=2, ensure_ascii=False)
102
-
103
- ctx.log.info(f"[Kiro] Captured response: {flow.response.status_code}")
104
-
105
-
106
- # 如果直接运行此脚本
107
- if __name__ == "__main__":
108
- print("""
109
- ╔══════════════════════════════════════════════════════════════════╗
110
- ║ Kiro IDE 请求抓取工具 ║
111
- ╠══════════════════════════════════════════════════════════════════╣
112
- ║ ║
113
- ║ 方法 1: 使用 mitmproxy (推荐) ║
114
- ║ ─────────────────────────────────────────────────────────────── ║
115
- ║ 1. 安装: pip install mitmproxy ║
116
- ║ 2. 运行: mitmproxy -s capture_kiro.py ║
117
- ║ 或: mitmdump -s capture_kiro.py ║
118
- ║ 3. 设置 Kiro IDE 的代理为 127.0.0.1:8080 ║
119
- ║ 4. 安装 CA 证书: 访问 http://mitm.it ║
120
- ║ ║
121
- ║ 方法 2: 使用 Burp Suite ║
122
- ║ ─────────────────────────────────────────────────────────────── ║
123
- ║ 1. 启动 Burp Suite ║
124
- ║ 2. 设置代理监听 127.0.0.1:8080 ║
125
- ║ 3. 导出 CA 证书并安装到系统 ║
126
- ║ 4. 设置 Kiro IDE 的代理 ║
127
- ║ ║
128
- ║ 方法 3: 直接修改 Kiro IDE (最简单) ║
129
- ║ ─────────────────────────────────────────────────────────────── ║
130
- ║ 在 Kiro IDE 的设置中添加: ║
131
- ║ "http.proxy": "http://127.0.0.1:8080" ║
132
- ║ ║
133
- ║ 或者设置环境变量: ║
134
- ║ export HTTPS_PROXY=http://127.0.0.1:8080 ║
135
- ║ export HTTP_PROXY=http://127.0.0.1:8080 ║
136
- ║ export NODE_TLS_REJECT_UNAUTHORIZED=0 ║
137
- ║ ║
138
- ╚══════════════════════════════════════════════════════════════════╝
139
- """)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
KiroProxy/scripts/proxy_server.py DELETED
@@ -1,135 +0,0 @@
1
- #!/usr/bin/env python3
2
- """
3
- Kiro IDE 反向代理测试服务器
4
- 用于测试是否能成功拦截和转发 Kiro 的 API 请求
5
- """
6
-
7
- from fastapi import FastAPI, Request, Response
8
- from fastapi.responses import JSONResponse, StreamingResponse
9
- import httpx
10
- import uvicorn
11
- import json
12
- import logging
13
- from datetime import datetime
14
-
15
- # 配置日志
16
- logging.basicConfig(
17
- level=logging.INFO,
18
- format='%(asctime)s - %(levelname)s - %(message)s'
19
- )
20
- logger = logging.getLogger(__name__)
21
-
22
- app = FastAPI(title="Kiro Reverse Proxy Test")
23
-
24
- # 原始 Kiro API 地址(如果需要转发到真实服务器)
25
- KIRO_API_BASE = "https://api.kiro.dev"
26
-
27
- # 记录所有请求
28
- request_log = []
29
-
30
- @app.middleware("http")
31
- async def log_requests(request: Request, call_next):
32
- """记录所有进入的请求"""
33
- body = await request.body()
34
-
35
- log_entry = {
36
- "timestamp": datetime.now().isoformat(),
37
- "method": request.method,
38
- "url": str(request.url),
39
- "path": request.url.path,
40
- "headers": dict(request.headers),
41
- "body": body.decode('utf-8', errors='ignore')[:2000] if body else None
42
- }
43
-
44
- request_log.append(log_entry)
45
- logger.info(f"📥 {request.method} {request.url.path}")
46
- logger.info(f" Headers: {dict(request.headers)}")
47
- if body:
48
- logger.info(f" Body: {body.decode('utf-8', errors='ignore')[:500]}...")
49
-
50
- response = await call_next(request)
51
- return response
52
-
53
- @app.get("/")
54
- async def root():
55
- """健康检查"""
56
- return {"status": "ok", "message": "Kiro Proxy Server Running", "requests_logged": len(request_log)}
57
-
58
- @app.get("/logs")
59
- async def get_logs():
60
- """查看所有记录的请求"""
61
- return {"total": len(request_log), "requests": request_log[-50:]}
62
-
63
- @app.get("/clear")
64
- async def clear_logs():
65
- """清空日志"""
66
- request_log.clear()
67
- return {"message": "Logs cleared"}
68
-
69
- # 模拟认证成功响应
70
- @app.api_route("/auth/{path:path}", methods=["GET", "POST", "PUT", "DELETE", "OPTIONS"])
71
- async def mock_auth(request: Request, path: str):
72
- """模拟认证端点"""
73
- logger.info(f"🔐 Auth request: {path}")
74
- return JSONResponse({
75
- "success": True,
76
- "token": "mock-token-for-testing",
77
- "expires_in": 3600
78
- })
79
-
80
- # 模拟 AI 对话端点
81
- @app.post("/v1/chat/completions")
82
- async def mock_chat_completions(request: Request):
83
- """模拟 OpenAI 兼容的聊天接口"""
84
- body = await request.json()
85
- logger.info(f"💬 Chat request: {json.dumps(body, ensure_ascii=False)[:500]}")
86
-
87
- # 返回模拟响应
88
- return JSONResponse({
89
- "id": "chatcmpl-test",
90
- "object": "chat.completion",
91
- "created": int(datetime.now().timestamp()),
92
- "model": "kiro-proxy-test",
93
- "choices": [{
94
- "index": 0,
95
- "message": {
96
- "role": "assistant",
97
- "content": "🎉 反向代理测试成功!你的请求已被成功拦截。"
98
- },
99
- "finish_reason": "stop"
100
- }],
101
- "usage": {"prompt_tokens": 10, "completion_tokens": 20, "total_tokens": 30}
102
- })
103
-
104
- # 捕获所有其他请求
105
- @app.api_route("/{path:path}", methods=["GET", "POST", "PUT", "DELETE", "OPTIONS", "PATCH"])
106
- async def catch_all(request: Request, path: str):
107
- """捕获所有其他请求并记录"""
108
- body = await request.body()
109
-
110
- logger.info(f"🎯 Caught: {request.method} /{path}")
111
-
112
- return JSONResponse({
113
- "proxy_status": "intercepted",
114
- "method": request.method,
115
- "path": f"/{path}",
116
- "message": "请求已被反向代理捕获",
117
- "headers_received": dict(request.headers)
118
- })
119
-
120
- if __name__ == "__main__":
121
- print("""
122
- ╔══════════════════════════════════════════════════════════════╗
123
- ║ Kiro IDE 反向代理测试服务器 ║
124
- ╠══════════════════════════════════════════════════════════════╣
125
- ║ 端口: 8000 ║
126
- ║ 查看日志: http://127.0.0.1:8000/logs ║
127
- ║ 清空日志: http://127.0.0.1:8000/clear ║
128
- ╠══════════════════════════════════════════════════════════════╣
129
- ║ 使用方法: ║
130
- ║ 1. 修改 Kiro 的 JS 源码,将 api.kiro.dev 替换为 127.0.0.1:8000 ║
131
- ║ 2. 或者修改 /etc/hosts 添加: 127.0.0.1 api.kiro.dev ║
132
- ║ 3. 启动 Kiro,观察此终端的日志输出 ║
133
- ╚════════════════════════════���═════════════════════════════════╝
134
- """)
135
- uvicorn.run(app, host="0.0.0.0", port=8000)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
README.md CHANGED
@@ -1,6 +1,6 @@
1
  ---
2
  title: KiroProxy
3
- emoji: 🚀
4
  colorFrom: blue
5
  colorTo: purple
6
  sdk: docker
@@ -10,107 +10,412 @@ short_description: Kiro IDE API 反向代理服务器 - 支持多账号管理和
10
  app_port: 7860
11
  ---
12
 
13
- # KiroProxy
 
 
14
 
15
- 🚀 **Kiro IDE API 反向代理服务器** - 支持多账号管理和多协议转换
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
16
 
17
  ## 功能特性
18
 
19
- - **多协议支持** - 兼容 OpenAI、Anthropic、Gemini 三种 API 协议
20
- - **多账号轮询** - 支持随机、轮询、会话粘性等选择策略
21
- - **自动 Token 管理** - 后台自动刷新即将过期 Token
22
- - **配额管理** - 智能配额调度和超限冷却
23
- - **Web UI** - 内置管理界面,支持账号管理、监控、日志
24
- - **数据库支持** - 支持文件系统和远程SQL数据库存储
25
- - **管理员** - 安全的密码认证和会话管理系统
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
26
 
27
  ## 快速开始
28
 
29
- ### 本地运行
 
 
 
 
30
 
31
  ```bash
32
  # 克隆项目
33
- git clone <repository-url>
34
- cd KiroProxy
 
 
 
 
35
 
36
  # 安装依赖
37
  pip install -r requirements.txt
38
 
39
- # 启动服务
40
- python run.py 8080
 
 
 
41
  ```
42
 
43
- ### Docker 部署
 
 
 
 
44
 
45
  ```bash
46
- # 构建镜像
47
- docker build -t kiroproxy .
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
48
 
49
- # 运行容器
50
- docker run -p 7860:7860 \
51
- -e ADMIN_PASSWORD=your_password \
52
- -e DATABASE_URL=postgresql://user:pass@host:5432/db \
53
- kiroproxy
54
  ```
55
 
56
- ### Hugging Face Space 部署
57
 
58
- 1. Fork 此项目到你的 Hugging Face Space
59
- 2. 在 Space 设置中配置环境��量:
60
- - `ADMIN_PASSWORD`: 管理员密码
61
- - `DATABASE_URL`: 远程数据库连接(可选)
62
- 3. Space 将自动构建和部署
63
 
64
- ## 环境变量配置
 
 
 
65
 
66
- 参考 `.env.example` 文件了解所有可用的环境变量配置选项。
 
 
67
 
68
- ### 核心配置
69
 
70
- - `ADMIN_PASSWORD`: 管理员面板密码(强烈推荐设置)
71
- - `DATABASE_URL`: 数据库连接URL(可选,支持PostgreSQL/MySQL/SQLite)
72
- - `PORT`: 服务器端口(默认:8080,Hugging Face Space使用7860)
 
 
73
 
74
- ## 管理员面板
75
 
76
- 访问 `http://localhost:8080` 进入管理界面。
77
 
78
- 首次启动时:
79
- - 如果设置了 `ADMIN_PASSWORD` 环境变量,使用该密码登录
80
- - 如果未设置,系统会自动生成随机密码并在启动日志中显示
81
 
82
- ## 数据存储
83
 
84
- ### 文件系统存储默认
85
- - 配置存储在 `~/.kiro-proxy/` 目录
86
- - 适合本地开发和小规模部署
87
 
88
- ### 远程数据库存储
89
- - 支持 PostgreSQL、MySQL、SQLite
90
- - 适合生产环境和多实例部署
91
- - 通过 `DATABASE_URL` 环境变量配置
 
 
 
 
 
 
 
 
 
 
 
 
92
 
93
- ## API 协议支持
94
 
95
- ### OpenAI 兼容
 
 
 
 
 
 
96
  ```
97
- POST /v1/chat/completions
 
 
 
 
 
 
 
 
98
  ```
99
 
100
- ### Anthropic 兼容
 
 
 
 
 
 
 
 
101
  ```
102
- POST /v1/messages
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
103
  ```
104
 
105
- ### Gemini 兼容
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
106
  ```
107
- POST /v1beta/models/{model}:generateContent
 
 
 
 
 
 
 
 
108
  ```
109
 
110
- ## 许可证
111
 
112
- MIT License
113
 
114
- ## 贡献
115
 
116
- 欢迎提交 Issue Pull Request!
 
1
  ---
2
  title: KiroProxy
3
+ emoji: ??
4
  colorFrom: blue
5
  colorTo: purple
6
  sdk: docker
 
10
  app_port: 7860
11
  ---
12
 
13
+ <p align="center">
14
+ <img src="assets/icon.svg" width="80" height="96" alt="Kiro Proxy">
15
+ </p>
16
 
17
+ <h1 align="center">Kiro API Proxy</h1>
18
+
19
+ <p align="center">
20
+ Kiro IDE API 反向代理服务器,支持多账号轮询、Token 自动刷新、配额管理
21
+ </p>
22
+
23
+ <p align="center">
24
+ <a href="#功能特性">功能</a> ?
25
+ <a href="#快速开始">快速开始</a> ?
26
+ <a href="#cli-配置">CLI 配置</a> ?
27
+ <a href="#api-端点">API</a> ?
28
+ <a href="#许可证">许可证</a>
29
+ </p>
30
+
31
+ ---
32
+
33
+ > **?? 测试说明**
34
+ >
35
+ > 本项目支持 **Claude Code**、**Codex CLI**、**Gemini CLI** 三种客户端,工具调用功能已全面支持。
36
 
37
  ## 功能特性
38
 
39
+ ### 核心功能
40
+ - **多协议支持** - OpenAI / Anthropic / Gemini 三种协议兼容
41
+ - **完整工具调用** - 三种协议工具调用功能全面支持
42
+ - **图片** - 支持 Claude Code / Codex CLI 图片输入
43
+ - **网络搜索** - 支持 Claude Code / Codex CLI 网络搜索工具
44
+ - **思考功能** - 支持 Claude 的扩展思考功能(Extended Thinking)
45
+ - **多账号轮询(默随机)** - 每次请求随机切换账号,分散压力,避免单账号 RPM 过高
46
+ - **会话粘性(可选)** - 非 `random` 策略下,同一会话 60 秒内使用同一账号,保持上下文
47
+ - **Web UI** - 简洁的管理界面,支持监控、日志、设置
48
+
49
+ ### v1.7.1 新功能
50
+ - **Windows 支持补强** - 注册表浏览器检测 + PATH 回退,兼容便携版
51
+ - **打包资源修复** - PyInstaller 打包后可正常加载图标与内置文档
52
+ - **Token 扫描稳定性** - Windows 路径编码处理修复
53
+
54
+ ### v1.6.3 新功能
55
+ - **命令行工具 (CLI)** - 无 GUI 服务器也能轻松管理
56
+ - `python run.py accounts list` - 列出账号
57
+ - `python run.py accounts export/import` - 导出/导入账号
58
+ - `python run.py accounts add` - 交互式添加 Token
59
+ - `python run.py accounts scan` - 扫描本地 Token
60
+ - `python run.py login google/github` - 命令行登录
61
+ - `python run.py login remote` - 生成远程登录链接
62
+ - **远程登录链接** - 在有浏览器的机器上完成授权,Token 自动同步
63
+ - **账号导入导出** - 跨机器迁移账号配置
64
+ - **手动添加 Token** - 直接粘贴 accessToken/refreshToken
65
+
66
+ ### v1.6.2 新功能
67
+ - **Codex CLI 完整支持** - 使用 OpenAI Responses API (`/v1/responses`)
68
+ - 完整工具调用支持(shell、file 等所有工具)
69
+ - 图片输入支持(`input_image` 类型)
70
+ - 网络搜索支持(`web_search` 工具)
71
+ - 错误代码映射(rate_limit、context_length 等)
72
+ - **Claude Code 增强** - 图片理解和网络搜索完整支持
73
+ - 支持 Anthropic 和 OpenAI 两种图片格式
74
+ - 支持 `web_search` / `web_search_20250305` 工具
75
+
76
+ ### v1.6.1 新功能
77
+ - **请求限速** - 通过限制请求频率降低账号封禁风险
78
+ - 每账号最小请求间隔
79
+ - 每账号每分钟最大请求数
80
+ - 全局每分钟最大请求数
81
+ - WebUI 设置页面可配置
82
+ - **账号封禁检测** - 自动检测 TEMPORARILY_SUSPENDED 错误
83
+ - 友好的错误日志输出
84
+ - 自动禁用被封禁账号
85
+ - 自动切换到其他可用账号
86
+ - **统一错误处理** - 三种协议使用统一的错误分类和处理
87
+
88
+ ### v1.6.0 功能
89
+ - **历史消息管理** - 4 种策略处理对话长度限制,可自由组合
90
+ - 自动截断:发送前优先保留最新上下文并摘要前文,必要时按数量/字符数截断
91
+ - 智能摘要:用 AI 生成早期对话摘要,保留关键信息
92
+ - 摘要缓存:历史变化不大时复用最近摘要,减少重复 LLM 调用(默认启用)
93
+ - 错误重试:遇到长度错误时自动截断重试(默认启用)
94
+ - 预估检测:预估 token 数量,超限预先截断
95
+ - **Gemini 工具调用** - 完整支持 functionDeclarations/functionCall/functionResponse
96
+ - **设置页面** - WebUI 新增设置标签页,可配置历史消息管理策略
97
+
98
+ ### v1.5.0 功能
99
+ - **用量查询** - 查询账号配额使用情况,显示已用/余额/使用率
100
+ - **多登录方式** - 支持 Google / GitHub / AWS Builder ID 三种登录方式
101
+ - **流量监控** - 完整的 LLM 请求监控,支持搜索、过滤、导出
102
+ - **浏览器选择** - 自动检测已安��浏览器,支持无痕模式
103
+ - **文档中心** - 内置帮助文档,左侧目录 + 右侧 Markdown 渲染
104
+
105
+ ### v1.4.0 功能
106
+ - **Token 预刷新** - 后台每 5 分钟检查,提前 15 分钟自动刷新
107
+ - **健康检查** - 每 10 分钟检测账号可用性,自动标记状态
108
+ - **请求统计增强** - 按账号/模型统计,24 小时趋势
109
+ - **请求重试机制** - 网络错误/5xx 自动重试,指数退避
110
+
111
+ ## 工具调用支持
112
+
113
+ | 功能 | Anthropic (Claude Code) | OpenAI (Codex CLI) | Gemini |
114
+ |------|------------------------|-------------------|--------|
115
+ | 工具定义 | ? `tools` | ? `tools.function` | ? `functionDeclarations` |
116
+ | 工具调用响应 | ? `tool_use` | ? `tool_calls` | ? `functionCall` |
117
+ | 工具结果 | ? `tool_result` | ? `tool` 角色消息 | ? `functionResponse` |
118
+ | 强制工具调用 | ? `tool_choice` | ? `tool_choice` | ? `toolConfig.mode` |
119
+ | 工具数量限制 | ? 50 个 | ? 50 个 | ? 50 个 |
120
+ | 历史消息修复 | ? | ? | ? |
121
+ | 图片理解 | ? | ? | ? |
122
+ | 网络搜索 | ? | ? | ? |
123
+
124
+ ## 已知限制
125
+
126
+ ### 对话长度限制
127
+
128
+ Kiro API 有输入长度限制。当对话历史过长时,会返回错误:
129
+
130
+ ```
131
+ Input is too long. (CONTENT_LENGTH_EXCEEDS_THRESHOLD)
132
+ ```
133
+
134
+ #### 自动处理(v1.6.0+)
135
+
136
+ 代理内置了历史消息管理功能,可在「设置」页面配置:
137
+
138
+ - **错误重试**(默认):遇到长度错误时自动截断并重试
139
+ - **智能摘要**:用 AI 生成早期对话摘要,保留关键信息
140
+ - **摘要缓存**(默认):历史变化不大时复用最近摘要,减少重复 LLM 调用
141
+ - **自动截断**:每次请求前优先保留最新上下文并摘要前文,必要时按数量/字符数截断
142
+ - **预估检测**:预估 token 数量,超限预先截断
143
+
144
+ 如需 **关闭自动压缩/重试**(超限时直接报错),可设置环境变量 `KIROPROXY_HISTORY_ERROR_RETRY=0`,或将历史配置的 `strategies` 中移除 `error_retry`。
145
+
146
+ 摘要缓存可通过以下配置项调整(默认值):
147
+ - `summary_cache_enabled`: `true`
148
+ - `summary_cache_min_delta_messages`: `3`
149
+ - `summary_cache_min_delta_chars`: `4000`
150
+ - `summary_cache_max_age_seconds`: `180`
151
+
152
+ #### 手动处理
153
+
154
+ 1. 在 Claude Code 中输入 `/clear` 清空对话历史
155
+ 2. 告诉 AI 你之前在做什么,它会读取代码文件恢复上下文
156
 
157
  ## 快速开始
158
 
159
+ ### 方式一:下载预编译版
160
+
161
+ 从 [Releases](../../releases) 下载对应平台的安装包,解压后直接运行。
162
+
163
+ ### 方式二:从源码运行
164
 
165
  ```bash
166
  # 克隆项目
167
+ git clone https://github.com/yourname/kiro-proxy.git
168
+ cd kiro-proxy
169
+
170
+ # 创建虚拟环境
171
+ python -m venv venv
172
+ source venv/bin/activate # Windows: venv\Scripts\activate
173
 
174
  # 安装依赖
175
  pip install -r requirements.txt
176
 
177
+ # 运行
178
+ python run.py
179
+
180
+ # 或指定端口
181
+ python run.py 8081
182
  ```
183
 
184
+ 启动后访问 http://localhost:8080
185
+
186
+ ### 命令行工具 (CLI)
187
+
188
+ 无 GUI 服务器可使用 CLI 管理账号:
189
 
190
  ```bash
191
+ # 账号管理
192
+ python run.py accounts list # 列出账号
193
+ python run.py accounts export -o acc.json # 导出账号
194
+ python run.py accounts import acc.json # 导入账号
195
+ python run.py accounts add # 交互式添加 Token
196
+ python run.py accounts scan --auto # 扫描并自动添加本地 Token
197
+
198
+ # 登录
199
+ python run.py login google # Google 登录
200
+ python run.py login github # GitHub 登录
201
+ python run.py login remote --host myserver.com:8080 # 生成远程登录链接
202
+
203
+ # 服务
204
+ python run.py serve # 启动服务 (默认 8080)
205
+ python run.py serve -p 8081 # 指定端口
206
+ python run.py status # 查看状态
207
+ ```
208
+
209
+ ### 登录获取 Token
210
+
211
+ **方式一:在线登录(推荐)**
212
+ 1. 打开 Web UI,点击「在线登录」
213
+ 2. 选择登录方式:Google / GitHub / AWS Builder ID
214
+ 3. 在浏览器中完成授权
215
+ 4. 账号自动添加
216
+
217
+ **方式二:扫描 Token**
218
+ 1. 打开 Kiro IDE,使用 Google/GitHub 账号登录
219
+ 2. 登录成功后 token 自动保存到 `~/.aws/sso/cache/`
220
+ 3. 在 Web UI 点击「扫描 Token」添加账号
221
+
222
+ ## CLI 配置
223
+
224
+ ### 模型对照表
225
+
226
+ | Kiro 模型 | 能力 | Claude Code | Codex |
227
+ |-----------|------|-------------|-------|
228
+ | `claude-sonnet-4` | ??? 推荐 | `claude-sonnet-4` | `gpt-4o` |
229
+ | `claude-sonnet-4.5` | ???? 更强 | `claude-sonnet-4.5` | `gpt-4o` |
230
+ | `claude-haiku-4.5` | ? 快速 | `claude-haiku-4.5` | `gpt-4o-mini` |
231
+
232
+ ### Claude Code 配置
233
 
234
+ ```
235
+ 名称: Kiro Proxy
236
+ API Key: any
237
+ Base URL: http://localhost:8080
238
+ 模型: claude-sonnet-4
239
  ```
240
 
241
+ ### Codex 配置
242
 
243
+ Codex CLI 使用 OpenAI Responses API,配置如下:
 
 
 
 
244
 
245
+ ```bash
246
+ # 设置环境变量
247
+ export OPENAI_API_KEY=any
248
+ export OPENAI_BASE_URL=http://localhost:8080/v1
249
 
250
+ # 运行 Codex
251
+ codex
252
+ ```
253
 
254
+ 或在 `~/.codex/config.toml` 中配置
255
 
256
+ ```toml
257
+ [providers.openai]
258
+ api_key = "any"
259
+ base_url = "http://localhost:8080/v1"
260
+ ```
261
 
262
+ ## 思考功能支持
263
 
264
+ ### 什么是思考功能
265
 
266
+ 思考功能(Extended Thinking)允许 Claude 在生成回答前展示其思考过程,帮助用户理解 AI 的推理步骤。
 
 
267
 
268
+ ### 如何使用
269
 
270
+ 在请求中添加 `thinking`或对应协议的 thinking 配置即可启用:
 
 
271
 
272
+ ```json
273
+ {
274
+ "model": "claude-sonnet-4.5",
275
+ "messages": [
276
+ {
277
+ "role": "user",
278
+ "content": "解释一下量子计算的原理"
279
+ }
280
+ ],
281
+ "thinking": {
282
+ "thinking_type": "enabled",
283
+ "budget_tokens": 20000
284
+ },
285
+ "stream": true
286
+ }
287
+ ```
288
 
289
+ OpenAI Chat Completions (`POST /v1/chat/completions`) 也支持
290
 
291
+ ```json
292
+ {
293
+ "model": "gpt-4o",
294
+ "messages": [{"role": "user", "content": "解释一下量子计算的原理"}],
295
+ "thinking": { "type": "enabled" },
296
+ "stream": true
297
+ }
298
  ```
299
+
300
+ OpenAI Responses (`POST /v1/responses`) 也支持:
301
+
302
+ ```json
303
+ {
304
+ "model": "gpt-4o",
305
+ "input": "解释一下量子计算的原理",
306
+ "thinking": { "type": "enabled" }
307
+ }
308
  ```
309
 
310
+ Gemini generateContent (`POST /v1/models/{model}:generateContent`) 也支持:
311
+
312
+ ```json
313
+ {
314
+ "contents": [{"role": "user", "parts": [{"text": "解释一下量子计算的原理"}]}],
315
+ "generationConfig": {
316
+ "thinkingConfig": { "includeThoughts": true }
317
+ }
318
+ }
319
  ```
320
+
321
+ ### 参数说明
322
+
323
+ - `thinking_type`: 思考类型,设为 `"enabled"` 启用思考功能
324
+ - `budget_tokens`: 思考过程的 token 预算(不传则视为无限制)
325
+
326
+ ### 响应格式
327
+
328
+ 启用思考功能后,流式响应会包含两种内容块:
329
+
330
+ 1. **思考块**(type: "thinking"):展示 AI 的思考过程
331
+ 2. **文本块**(type: "text"):最终的回答内容
332
+
333
+ 示例响应:
334
+ ```
335
+ data: {"type":"content_block_start","index":1,"content_block":{"type":"thinking","thinking":""}}
336
+ data: {"type":"content_block_delta","index":1,"delta":{"type":"thinking_delta","thinking":"让我思考一下量子计算的原理..."}}
337
+ data: {"type":"content_block_stop","index":1}
338
+ data: {"type":"content_block_start","index":0,"content_block":{"type":"text","text":""}}
339
+ data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"量子计算是一种..."}}
340
+ data: {"type":"content_block_stop","index":0}
341
  ```
342
 
343
+ ## API 端点
344
+
345
+ | 协议 | 端点 | 用途 |
346
+ |------|------|------|
347
+ | OpenAI | `POST /v1/chat/completions` | Chat Completions API |
348
+ | OpenAI | `POST /v1/responses` | Responses API (Codex CLI) |
349
+ | OpenAI | `GET /v1/models` | 模型列表 |
350
+ | Anthropic | `POST /v1/messages` | Claude Code |
351
+ | Anthropic | `POST /v1/messages/count_tokens` | Token 计数 |
352
+ | Gemini | `POST /v1/models/{model}:generateContent` | Gemini CLI |
353
+
354
+ ### 管理 API
355
+
356
+ | 端点 | 方法 | 说明 |
357
+ |------|------|------|
358
+ | `/api/accounts` | GET | 获取所有账号状态 |
359
+ | `/api/accounts/{id}` | GET | 获取账号详情 |
360
+ | `/api/accounts/{id}/usage` | GET | 获取账号用量信息 |
361
+ | `/api/accounts/{id}/refresh` | POST | 刷新账号 Token |
362
+ | `/api/accounts/{id}/restore` | POST | 恢复账号(从冷却状态) |
363
+ | `/api/accounts/refresh-all` | POST | 刷新所有即将过期的 Token |
364
+ | `/api/flows` | GET | 获取流量记录 |
365
+ | `/api/flows/stats` | GET | 获取流量统计 |
366
+ | `/api/flows/{id}` | GET | 获取流量详情 |
367
+ | `/api/quota` | GET | 获取配额状态 |
368
+ | `/api/stats` | GET | 获取统计信息 |
369
+ | `/api/health-check` | POST | 手动触发健康检查 |
370
+ | `/api/browsers` | GET | 获取可用浏览器列表 |
371
+ | `/api/docs` | GET | 获取文档列表 |
372
+ | `/api/docs/{id}` | GET | 获取文档内容 |
373
+
374
+ ## 项目结构
375
+
376
+ ```
377
+ .
378
+ ├── run.py
379
+ ├── build.py
380
+ ├── pyproject.toml
381
+ ├── requirements.txt
382
+ ├── kiro_proxy/
383
+ │ ├── main.py # FastAPI 应用入口
384
+ │ ├── config.py # 全局配置
385
+ │ ├── converters/ # 协议转换
386
+ │ ├── core/ # 核心模块
387
+ │ ├── credential/ # 凭证管理
388
+ │ ├── auth/ # 认证模块
389
+ │ ├── handlers/ # API 处理器
390
+ │ │ ├── anthropic/ # /v1/messages
391
+ │ │ ├── admin/ # 管理 API
392
+ │ │ ├── openai.py # /v1/chat/completions
393
+ │ │ ├── responses.py # /v1/responses (Codex CLI)
394
+ │ │ └── gemini.py # /v1/models/{model}:generateContent
395
+ │ ├── routers/ # 路由层
396
+ │ ├── web/ # Web UI
397
+ │ └── docs/ # 内置文档
398
+ ├── assets/ # 资源文件
399
+ ├── legacy/ # 兼容旧入口
400
+ ├── scripts/ # 辅助脚本
401
+ ├── examples/ # 示例
402
+ └── tests/ # 测试
403
  ```
404
+
405
+ ## 构建
406
+
407
+ ```bash
408
+ # 安装构建依赖
409
+ pip install pyinstaller
410
+
411
+ # 构建
412
+ python build.py
413
  ```
414
 
415
+ 输出文件在 `dist/` 目录。
416
 
417
+ ## 免责声明
418
 
419
+ 本项目仅供学习研究,禁止商用。使用本项目产生的任何后果由使用者自行承担,与作者无关。
420
 
421
+ 本项目与 Kiro / AWS / Anthropic 官方无关。
README_HF.md DELETED
@@ -1,9 +0,0 @@
1
- title: KiroProxy
2
- emoji: 🚀
3
- colorFrom: blue
4
- colorTo: purple
5
- sdk: docker
6
- pinned: false
7
- license: mit
8
- short_description: Kiro IDE API 反向代理服务器 - 支持多账号管理和多协议转换
9
- app_port: 7860
 
 
 
 
 
 
 
 
 
 
{KiroProxy/assets → assets}/icon.iconset/icon_128x128.png RENAMED
File without changes
{KiroProxy/assets → assets}/icon.iconset/icon_16x16.png RENAMED
File without changes
{KiroProxy/assets → assets}/icon.iconset/icon_256x256.png RENAMED
File without changes
{KiroProxy/assets → assets}/icon.iconset/icon_32x32.png RENAMED
File without changes
{KiroProxy/assets → assets}/icon.iconset/icon_512x512.png RENAMED
File without changes
{KiroProxy/assets → assets}/icon.iconset/icon_64x64.png RENAMED
File without changes
{KiroProxy/assets → assets}/icon.png RENAMED
File without changes
{KiroProxy/assets → assets}/icon.svg RENAMED
File without changes
KiroProxy/build.py → build.py RENAMED
File without changes
{KiroProxy/examples → examples}/quota_display_example.py RENAMED
File without changes
{KiroProxy/examples → examples}/test_quota_display.html RENAMED
File without changes
KiroProxy/kiro.svg → kiro.svg RENAMED
File without changes
{KiroProxy/kiro_proxy → kiro_proxy}/__init__.py RENAMED
File without changes
{KiroProxy/kiro_proxy → kiro_proxy}/__main__.py RENAMED
File without changes
{KiroProxy/kiro_proxy → kiro_proxy}/auth/__init__.py RENAMED
File without changes
{KiroProxy/kiro_proxy → kiro_proxy}/auth/device_flow.py RENAMED
File without changes
{KiroProxy/kiro_proxy → kiro_proxy}/cli.py RENAMED
File without changes
{KiroProxy/kiro_proxy → kiro_proxy}/config.py RENAMED
File without changes
{KiroProxy/kiro_proxy → kiro_proxy}/converters/__init__.py RENAMED
File without changes
{KiroProxy/kiro_proxy → kiro_proxy}/core/__init__.py RENAMED
File without changes
{KiroProxy/kiro_proxy → kiro_proxy}/core/account.py RENAMED
File without changes
{KiroProxy/kiro_proxy → kiro_proxy}/core/account_selector.py RENAMED
File without changes
{KiroProxy/kiro_proxy → kiro_proxy}/core/admin_auth.py RENAMED
File without changes
{KiroProxy/kiro_proxy → kiro_proxy}/core/auth_middleware.py RENAMED
File without changes
{KiroProxy/kiro_proxy → kiro_proxy}/core/browser.py RENAMED
File without changes
{KiroProxy/kiro_proxy → kiro_proxy}/core/database.py RENAMED
File without changes
{KiroProxy/kiro_proxy → kiro_proxy}/core/error_handler.py RENAMED
File without changes
{KiroProxy/kiro_proxy → kiro_proxy}/core/flow_monitor.py RENAMED
File without changes
{KiroProxy/kiro_proxy → kiro_proxy}/core/history_manager.py RENAMED
File without changes
{KiroProxy/kiro_proxy → kiro_proxy}/core/kiro_api.py RENAMED
File without changes
{KiroProxy/kiro_proxy → kiro_proxy}/core/persistence.py RENAMED
File without changes
{KiroProxy/kiro_proxy → kiro_proxy}/core/protocol_handler.py RENAMED
File without changes
{KiroProxy/kiro_proxy → kiro_proxy}/core/quota_cache.py RENAMED
File without changes
{KiroProxy/kiro_proxy → kiro_proxy}/core/quota_scheduler.py RENAMED
File without changes