ai-agent-router 0.1.21 → 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/.next/BUILD_ID +1 -1
- package/.next/build-manifest.json +2 -2
- package/.next/fallback-build-manifest.json +2 -2
- package/.next/server/app/_global-error.html +2 -2
- package/.next/server/app/_global-error.rsc +1 -1
- package/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/_not-found.html +1 -1
- package/.next/server/app/_not-found.rsc +1 -1
- package/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
- package/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
- package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/api/config/route.js.nft.json +1 -1
- package/.next/server/app/api/gateway/[...path]/route.js.nft.json +1 -1
- package/.next/server/app/api/gateway/models/route.js.nft.json +1 -1
- package/.next/server/app/api/gateway/route.js.nft.json +1 -1
- package/.next/server/app/api/ide/claude/apply/route.js.nft.json +1 -1
- package/.next/server/app/api/ide/claude/available-models/route.js.nft.json +1 -1
- package/.next/server/app/api/ide/claude/save/route.js.nft.json +1 -1
- package/.next/server/app/api/ide/claude/status/route.js.nft.json +1 -1
- package/.next/server/app/api/ide/claude/test/route.js.nft.json +1 -1
- package/.next/server/app/api/logs/route.js.nft.json +1 -1
- package/.next/server/app/api/models/route.js.nft.json +1 -1
- package/.next/server/app/api/providers/route.js.nft.json +1 -1
- package/.next/server/app/api/providers/test/route.js.nft.json +1 -1
- package/.next/server/app/api/service/force-stop/route.js.nft.json +1 -1
- package/.next/server/app/api/service/start/route.js.nft.json +1 -1
- package/.next/server/app/api/service/status/route.js.nft.json +1 -1
- package/.next/server/app/api/service/stop/route.js.nft.json +1 -1
- package/.next/server/app/ide.html +1 -1
- package/.next/server/app/ide.rsc +1 -1
- package/.next/server/app/ide.segments/_full.segment.rsc +1 -1
- package/.next/server/app/ide.segments/_head.segment.rsc +1 -1
- package/.next/server/app/ide.segments/_index.segment.rsc +1 -1
- package/.next/server/app/ide.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/ide.segments/ide/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/ide.segments/ide.segment.rsc +1 -1
- package/.next/server/app/index.html +1 -1
- package/.next/server/app/index.rsc +1 -1
- package/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/index.segments/_full.segment.rsc +1 -1
- package/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/.next/server/app/index.segments/_index.segment.rsc +1 -1
- package/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/logs/page_client-reference-manifest.js +1 -1
- package/.next/server/app/logs.html +1 -1
- package/.next/server/app/logs.rsc +2 -2
- package/.next/server/app/logs.segments/_full.segment.rsc +2 -2
- package/.next/server/app/logs.segments/_head.segment.rsc +1 -1
- package/.next/server/app/logs.segments/_index.segment.rsc +1 -1
- package/.next/server/app/logs.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/logs.segments/logs/__PAGE__.segment.rsc +2 -2
- package/.next/server/app/logs.segments/logs.segment.rsc +1 -1
- package/.next/server/app/models.html +1 -1
- package/.next/server/app/models.rsc +1 -1
- package/.next/server/app/models.segments/_full.segment.rsc +1 -1
- package/.next/server/app/models.segments/_head.segment.rsc +1 -1
- package/.next/server/app/models.segments/_index.segment.rsc +1 -1
- package/.next/server/app/models.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/models.segments/models/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/models.segments/models.segment.rsc +1 -1
- package/.next/server/app/providers.html +1 -1
- package/.next/server/app/providers.rsc +1 -1
- package/.next/server/app/providers.segments/_full.segment.rsc +1 -1
- package/.next/server/app/providers.segments/_head.segment.rsc +1 -1
- package/.next/server/app/providers.segments/_index.segment.rsc +1 -1
- package/.next/server/app/providers.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/providers.segments/providers/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/providers.segments/providers.segment.rsc +1 -1
- package/.next/server/chunks/[root-of-the-server]__1480f018._.js +1 -1
- package/.next/server/chunks/[root-of-the-server]__1480f018._.js.map +1 -1
- package/.next/server/chunks/[root-of-the-server]__1909f3aa._.js +1 -1
- package/.next/server/chunks/[root-of-the-server]__1909f3aa._.js.map +1 -1
- package/.next/server/chunks/[root-of-the-server]__1d4b7fc5._.js +1 -1
- package/.next/server/chunks/[root-of-the-server]__1d4b7fc5._.js.map +1 -1
- package/.next/server/chunks/[root-of-the-server]__372ef2bf._.js +1 -1
- package/.next/server/chunks/[root-of-the-server]__372ef2bf._.js.map +1 -1
- package/.next/server/chunks/[root-of-the-server]__3aaf963c._.js +1 -1
- package/.next/server/chunks/[root-of-the-server]__3aaf963c._.js.map +1 -1
- package/.next/server/chunks/[root-of-the-server]__6ce199d2._.js +1 -1
- package/.next/server/chunks/[root-of-the-server]__6ce199d2._.js.map +1 -1
- package/.next/server/chunks/[root-of-the-server]__772134c6._.js +1 -1
- package/.next/server/chunks/[root-of-the-server]__772134c6._.js.map +1 -1
- package/.next/server/chunks/[root-of-the-server]__7b77f523._.js +1 -1
- package/.next/server/chunks/[root-of-the-server]__7b77f523._.js.map +1 -1
- package/.next/server/chunks/[root-of-the-server]__c1b4b601._.js +18 -18
- package/.next/server/chunks/[root-of-the-server]__c1b4b601._.js.map +1 -1
- package/.next/server/chunks/[root-of-the-server]__ccfc7f1d._.js +1 -1
- package/.next/server/chunks/[root-of-the-server]__ccfc7f1d._.js.map +1 -1
- package/.next/server/chunks/ssr/src_app_logs_page_tsx_7b7b7b83._.js +1 -1
- package/.next/server/chunks/ssr/src_app_logs_page_tsx_7b7b7b83._.js.map +1 -1
- package/.next/server/pages/404.html +1 -1
- package/.next/server/pages/500.html +2 -2
- package/.next/static/chunks/{81c904164fe81379.js → b6b258e8582e47c4.js} +1 -1
- package/README.md +100 -111
- package/dist/src/app/api/gateway/[...path]/route.js +1 -1
- package/dist/src/app/api/gateway/route.js +1 -1
- package/dist/src/app/api/logs/route.js +2 -2
- package/dist/src/app/api/models/route.js +5 -5
- package/dist/src/app/api/providers/route.js +4 -4
- package/dist/src/app/api/providers/test/route.js +1 -1
- package/dist/src/app/api/service/start/route.js +1 -1
- package/dist/src/app/api/service/status/route.js +1 -1
- package/dist/src/app/api/service/stop/route.js +1 -1
- package/dist/src/app/logs/page.js +13 -1
- package/dist/src/cli/index.js +218 -20
- package/dist/src/db/database.js +35 -1
- package/dist/src/db/queries.js +6 -6
- package/dist/src/server/logger.js +22 -4
- package/package.json +2 -1
- package/src/app/api/gateway/[...path]/route.ts +1 -1
- package/src/app/api/gateway/route.ts +1 -1
- package/src/app/api/logs/route.ts +2 -2
- package/src/app/api/models/route.ts +5 -5
- package/src/app/api/providers/route.ts +4 -4
- package/src/app/api/providers/test/route.ts +1 -1
- package/src/app/api/service/start/route.ts +1 -1
- package/src/app/api/service/status/route.ts +1 -1
- package/src/app/api/service/stop/route.ts +1 -1
- package/src/app/logs/page.tsx +15 -5
- package/src/cli/index.ts +228 -25
- package/src/db/database.ts +34 -4
- package/src/db/queries.ts +6 -6
- package/src/server/logger.ts +19 -4
- /package/.next/static/{PkfqdzwOZgX-UhSNUuhdp → ryTeHAYUvjT1bYolc-x9Z}/_buildManifest.js +0 -0
- /package/.next/static/{PkfqdzwOZgX-UhSNUuhdp → ryTeHAYUvjT1bYolc-x9Z}/_clientMiddlewareManifest.json +0 -0
- /package/.next/static/{PkfqdzwOZgX-UhSNUuhdp → ryTeHAYUvjT1bYolc-x9Z}/_ssgManifest.js +0 -0
package/README.md
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
# AI Agent Router
|
|
2
2
|
|
|
3
|
-
一个统一的 API 网关,用于管理多个 AI 模型供应商(Anthropic、OpenAI、Gemini 等),为 Claude、
|
|
3
|
+
一个统一的 API 网关,用于管理多个 AI 模型供应商(Anthropic、OpenAI、Gemini 等),为 Claude、Cursor、Alma 等客户端提供统一接口。
|
|
4
4
|
|
|
5
5
|
## 功能特性
|
|
6
6
|
|
|
7
7
|
- ✅ 支持多种 AI 模型协议(OpenAI、Anthropic、Gemini)
|
|
8
|
-
- ✅
|
|
8
|
+
- ✅ 一键启动:前台 Web UI + 网关服务,默认后台运行
|
|
9
9
|
- ✅ Web 管理界面,可视化配置和管理
|
|
10
10
|
- ✅ 模型供应商管理(添加、编辑、删除)
|
|
11
11
|
- ✅ 模型管理(手动添加、自动拉取模型列表)
|
|
12
12
|
- ✅ 请求日志记录和查看(支持流式响应的美化展示)
|
|
13
13
|
- ✅ 双端点支持(`/v1/*` 和 `/api/gateway/*`)
|
|
14
|
-
- ✅ API Key
|
|
15
|
-
- ✅ CLI
|
|
16
|
-
- ✅ SQLite
|
|
14
|
+
- ✅ API Key 认证保护网关服务
|
|
15
|
+
- ✅ CLI 工具(`aar`),支持后台运行与 `aar stop` 停止
|
|
16
|
+
- ✅ SQLite 数据库,数据存储在 `~/.aar/`
|
|
17
17
|
|
|
18
18
|
## 安装
|
|
19
19
|
|
|
@@ -21,35 +21,69 @@
|
|
|
21
21
|
npm install -g ai-agent-router
|
|
22
22
|
```
|
|
23
23
|
|
|
24
|
+
安装后可使用命令 `aar` 或 `npx ai-agent-router`。
|
|
25
|
+
|
|
24
26
|
## 使用方法
|
|
25
27
|
|
|
26
|
-
###
|
|
28
|
+
### 启动服务(默认:后台运行 Web UI + 网关)
|
|
27
29
|
|
|
28
30
|
```bash
|
|
29
31
|
aar start
|
|
30
32
|
```
|
|
31
33
|
|
|
32
|
-
|
|
34
|
+
- **默认行为**:在后台同时启动 Web 管理界面和网关服务,关闭终端后进程继续运行。
|
|
35
|
+
- 前台 UI:`http://localhost:9527`
|
|
36
|
+
- 网关地址:`http://localhost:1357`(端口可在 Web 界面中配置)
|
|
37
|
+
|
|
38
|
+
启动后控制台会输出访问地址与网关状态,例如:
|
|
39
|
+
|
|
40
|
+
```
|
|
41
|
+
-------------------------------------------
|
|
42
|
+
AI Agent Router
|
|
43
|
+
-------------------------------------------
|
|
44
|
+
前台 UI: http://localhost:9527
|
|
45
|
+
网关地址: http://localhost:1357
|
|
46
|
+
网关状态: 运行中
|
|
47
|
+
运行模式: 后台运行(关闭终端不影响)
|
|
48
|
+
停止服务: aar stop
|
|
49
|
+
-------------------------------------------
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### 停止后台服务
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
aar stop
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
会停止由 `aar start` 在后台启动的 Web UI 与网关进程。
|
|
59
|
+
|
|
60
|
+
### 前台运行(附着当前终端)
|
|
33
61
|
|
|
34
|
-
|
|
62
|
+
若希望在前台运行、用 Ctrl+C 结束:
|
|
35
63
|
|
|
36
64
|
```bash
|
|
37
|
-
aar
|
|
65
|
+
aar start --no-background
|
|
38
66
|
```
|
|
39
67
|
|
|
40
|
-
|
|
68
|
+
### 仅启动 Web UI(不启动网关)
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
aar start --no-gateway
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
仅启动管理界面,网关可在 Web 界面里再手动启动。
|
|
41
75
|
|
|
42
76
|
### 配置选项
|
|
43
77
|
|
|
44
78
|
```bash
|
|
45
|
-
# 指定 Web UI
|
|
46
|
-
aar start
|
|
79
|
+
# 指定 Web UI 端口(默认 9527)
|
|
80
|
+
aar start -p 8080
|
|
47
81
|
|
|
48
|
-
#
|
|
49
|
-
aar start
|
|
82
|
+
# 指定网关端口(默认从配置或 1357)
|
|
83
|
+
aar start -g 3000
|
|
50
84
|
|
|
51
|
-
#
|
|
52
|
-
aar
|
|
85
|
+
# 组合:前台运行 + 自定义端口
|
|
86
|
+
aar start --no-background -p 9527 -g 1357
|
|
53
87
|
```
|
|
54
88
|
|
|
55
89
|
### 配置管理
|
|
@@ -58,93 +92,58 @@ aar gateway --port 3000
|
|
|
58
92
|
# 获取配置
|
|
59
93
|
aar config --get port
|
|
60
94
|
|
|
61
|
-
#
|
|
62
|
-
aar config --set port
|
|
63
|
-
|
|
64
|
-
# 设置 Gateway API Key(用于外部客户端认证)
|
|
95
|
+
# 设置配置(key 与 value 用空格分隔)
|
|
96
|
+
aar config --set port 1357
|
|
65
97
|
aar config --set api_key "your-secret-key"
|
|
66
98
|
```
|
|
67
99
|
|
|
100
|
+
常用配置项:`port`(网关端口)、`api_key`(网关 API Key,用于客户端认证)。
|
|
101
|
+
|
|
68
102
|
## Web 管理界面
|
|
69
103
|
|
|
70
|
-
启动后访问
|
|
104
|
+
启动后访问 **http://localhost:9527**,可以:
|
|
71
105
|
|
|
72
|
-
1.
|
|
106
|
+
1. **配置网关**:端口、API Key 等
|
|
73
107
|
2. **管理供应商**:添加、编辑、删除模型供应商
|
|
74
|
-
3.
|
|
75
|
-
4.
|
|
76
|
-
5.
|
|
77
|
-
|
|
78
|
-
## 添加供应商
|
|
79
|
-
|
|
80
|
-
1. 访问 Web 界面,进入"供应商"页面
|
|
81
|
-
2. 点击"添加供应商"
|
|
82
|
-
3. 填写信息:
|
|
83
|
-
- 名称:供应商名称(如 "OpenAI")
|
|
84
|
-
- 协议:选择协议类型(OpenAI、Anthropic、Gemini)
|
|
85
|
-
- Base URL:API 基础 URL(如 `https://api.openai.com/v1`)
|
|
86
|
-
- API Key:供应商的 API Key
|
|
87
|
-
|
|
88
|
-
## 添加模型
|
|
89
|
-
|
|
90
|
-
### 手动添加
|
|
108
|
+
3. **管理模型**:手动添加或一键拉取模型列表
|
|
109
|
+
4. **查看日志**:API 请求日志,含流式响应美化
|
|
110
|
+
5. **服务管理**:在界面内启动/停止网关(使用 `aar start` 时通常已自动启动)
|
|
91
111
|
|
|
92
|
-
|
|
93
|
-
2. 点击"手动添加"
|
|
94
|
-
3. 选择供应商,填写模型名称和模型 ID
|
|
112
|
+
## 添加供应商与模型
|
|
95
113
|
|
|
96
|
-
|
|
114
|
+
1. 打开 Web 界面 →「供应商」→「添加供应商」
|
|
115
|
+
2. 填写:名称、协议(OpenAI / Anthropic / Gemini)、Base URL、API Key
|
|
116
|
+
3. 在「模型」页可手动添加模型,或使用「拉取模型列表」从供应商拉取
|
|
97
117
|
|
|
98
|
-
|
|
99
|
-
2. 在"一键拉取模型列表"区域,点击对应供应商的"拉取模型"按钮
|
|
100
|
-
3. 系统会自动从供应商 API 拉取可用模型列表
|
|
118
|
+
## 使用网关 API
|
|
101
119
|
|
|
102
|
-
|
|
120
|
+
配置好供应商和模型后,客户端可请求网关(默认 `http://localhost:1357`)。
|
|
103
121
|
|
|
104
|
-
|
|
122
|
+
### OpenAI 兼容
|
|
105
123
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
Gateway 服务运行在 `http://localhost:1357`,支持两种兼容的端点:
|
|
109
|
-
|
|
110
|
-
#### OpenAI 兼容接口
|
|
111
|
-
|
|
112
|
-
```
|
|
124
|
+
```http
|
|
113
125
|
POST http://localhost:1357/v1/chat/completions
|
|
114
126
|
Content-Type: application/json
|
|
115
|
-
|
|
127
|
+
Authorization: Bearer your-gateway-api-key
|
|
116
128
|
|
|
117
129
|
{
|
|
118
130
|
"model": "gpt-4",
|
|
119
131
|
"messages": [...],
|
|
120
|
-
"stream": true
|
|
132
|
+
"stream": true
|
|
121
133
|
}
|
|
122
134
|
```
|
|
123
135
|
|
|
124
|
-
|
|
136
|
+
### 网关路径形式
|
|
125
137
|
|
|
126
|
-
```
|
|
138
|
+
```http
|
|
127
139
|
POST http://localhost:1357/api/gateway/v1/chat/completions
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
{
|
|
132
|
-
"model": "gpt-4",
|
|
133
|
-
"messages": [...],
|
|
134
|
-
"stream": true // 支持流式响应
|
|
135
|
-
}
|
|
136
|
-
```
|
|
137
|
-
|
|
138
|
-
### 模型列表
|
|
139
|
-
|
|
140
|
-
```
|
|
141
|
-
GET http://localhost:1357/v1/models
|
|
142
|
-
GET http://localhost:1357/api/gateway/models
|
|
140
|
+
GET http://localhost:1357/v1/models
|
|
141
|
+
GET http://localhost:1357/api/gateway/models
|
|
143
142
|
```
|
|
144
143
|
|
|
145
|
-
### Anthropic
|
|
144
|
+
### Anthropic 兼容
|
|
146
145
|
|
|
147
|
-
```
|
|
146
|
+
```http
|
|
148
147
|
POST http://localhost:1357/api/gateway/v1/messages
|
|
149
148
|
Content-Type: application/json
|
|
150
149
|
x-api-key: your-gateway-api-key
|
|
@@ -155,63 +154,53 @@ x-api-key: your-gateway-api-key
|
|
|
155
154
|
}
|
|
156
155
|
```
|
|
157
156
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
- 如果配置了 Gateway API Key,客户端需要在请求头中提供:`x-api-key: your-gateway-api-key`
|
|
161
|
-
- 支持 Bearer token 格式:`Authorization: Bearer your-gateway-api-key`
|
|
157
|
+
认证:若在配置中设置了 API Key,请求头需带 `x-api-key` 或 `Authorization: Bearer <key>`。
|
|
162
158
|
|
|
163
159
|
## 开发
|
|
164
160
|
|
|
165
|
-
### 本地开发
|
|
166
|
-
|
|
167
161
|
```bash
|
|
168
162
|
# 安装依赖
|
|
169
163
|
npm install
|
|
170
164
|
|
|
171
|
-
#
|
|
165
|
+
# 开发模式(仅 Next 前台,端口 9527)
|
|
172
166
|
npm run dev
|
|
173
167
|
|
|
174
168
|
# 构建
|
|
175
169
|
npm run build
|
|
176
170
|
|
|
177
|
-
#
|
|
171
|
+
# 生产启动(等同 aar start,需先 build)
|
|
178
172
|
npm start
|
|
179
173
|
```
|
|
180
174
|
|
|
181
175
|
### 项目结构
|
|
182
176
|
|
|
183
177
|
```
|
|
184
|
-
|
|
185
|
-
├──
|
|
186
|
-
│ ├──
|
|
187
|
-
│
|
|
188
|
-
│
|
|
189
|
-
│
|
|
190
|
-
│
|
|
191
|
-
|
|
192
|
-
│ ├──
|
|
193
|
-
│
|
|
194
|
-
│
|
|
195
|
-
|
|
196
|
-
│ ├──
|
|
197
|
-
│
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
│ └── cli/ # CLI 入口
|
|
202
|
-
│ ├── index.ts # 主 CLI 程序
|
|
203
|
-
│ └── gateway-server.ts # Gateway CLI 入口
|
|
204
|
-
├── package.json
|
|
205
|
-
└── README.md
|
|
178
|
+
src/
|
|
179
|
+
├── server/ # 网关与后端
|
|
180
|
+
│ ├── gateway.ts # 网关请求处理
|
|
181
|
+
│ ├── gateway-server.ts # 独立网关 HTTP 服务
|
|
182
|
+
│ ├── service-manager.ts # 进程/服务管理
|
|
183
|
+
│ ├── providers/ # 各协议适配
|
|
184
|
+
│ └── logger.ts # 请求日志
|
|
185
|
+
├── db/ # SQLite(~/.aar/gateway.db)
|
|
186
|
+
│ ├── database.ts
|
|
187
|
+
│ ├── schema.ts
|
|
188
|
+
│ └── queries.ts
|
|
189
|
+
├── app/ # Next.js App Router
|
|
190
|
+
│ ├── api/ # API 路由(gateway、config、service 等)
|
|
191
|
+
│ └── (页面组件)
|
|
192
|
+
└── cli/ # CLI
|
|
193
|
+
├── index.ts # aar start / stop / config
|
|
194
|
+
└── gateway-server.ts # 独立网关进程入口
|
|
206
195
|
```
|
|
207
196
|
|
|
208
197
|
## 技术栈
|
|
209
198
|
|
|
210
|
-
- **Next.js
|
|
199
|
+
- **Next.js** - 全栈与 Web UI
|
|
211
200
|
- **TypeScript** - 类型安全
|
|
212
|
-
- **Tailwind CSS** -
|
|
213
|
-
- **
|
|
214
|
-
- **Commander.js** - CLI
|
|
201
|
+
- **Tailwind CSS** - 样式
|
|
202
|
+
- **SQLite (sql.js)** - 本地存储
|
|
203
|
+
- **Commander.js** - CLI
|
|
215
204
|
|
|
216
205
|
## 许可证
|
|
217
206
|
|
|
@@ -219,4 +208,4 @@ MIT
|
|
|
219
208
|
|
|
220
209
|
## 贡献
|
|
221
210
|
|
|
222
|
-
欢迎提交 Issue
|
|
211
|
+
欢迎提交 Issue 与 Pull Request。
|
|
@@ -33,7 +33,7 @@ async function DELETE(request, { params }) {
|
|
|
33
33
|
}
|
|
34
34
|
async function handleRequest(request, params, method) {
|
|
35
35
|
try {
|
|
36
|
-
(0, database_1.getDatabase)();
|
|
36
|
+
await (0, database_1.getDatabase)();
|
|
37
37
|
const path = params.path.join('/');
|
|
38
38
|
const searchParams = request.nextUrl.searchParams;
|
|
39
39
|
// Get model ID from query
|
|
@@ -17,7 +17,7 @@ async function GET(request) {
|
|
|
17
17
|
}
|
|
18
18
|
async function handleGatewayRequestDirect(request, method) {
|
|
19
19
|
try {
|
|
20
|
-
(0, database_1.getDatabase)();
|
|
20
|
+
await (0, database_1.getDatabase)();
|
|
21
21
|
const searchParams = request.nextUrl.searchParams;
|
|
22
22
|
let modelId = searchParams.get('model') || searchParams.get('model_id');
|
|
23
23
|
const providerName = searchParams.get('provider');
|
|
@@ -35,7 +35,7 @@ function cleanLogData(log) {
|
|
|
35
35
|
}
|
|
36
36
|
async function GET(request) {
|
|
37
37
|
try {
|
|
38
|
-
(0, database_1.getDatabase)();
|
|
38
|
+
await (0, database_1.getDatabase)();
|
|
39
39
|
const { searchParams } = new URL(request.url);
|
|
40
40
|
const id = searchParams.get('id');
|
|
41
41
|
const limit = parseInt(searchParams.get('limit') || '100');
|
|
@@ -66,7 +66,7 @@ async function GET(request) {
|
|
|
66
66
|
}
|
|
67
67
|
async function DELETE(request) {
|
|
68
68
|
try {
|
|
69
|
-
(0, database_1.getDatabase)();
|
|
69
|
+
await (0, database_1.getDatabase)();
|
|
70
70
|
const { searchParams } = new URL(request.url);
|
|
71
71
|
const ids = searchParams.get('ids');
|
|
72
72
|
const clearAll = searchParams.get('clear_all') === 'true';
|
|
@@ -14,7 +14,7 @@ const providers_1 = require("@/server/providers");
|
|
|
14
14
|
exports.runtime = 'nodejs';
|
|
15
15
|
async function GET(request) {
|
|
16
16
|
try {
|
|
17
|
-
(0, database_1.getDatabase)();
|
|
17
|
+
await (0, database_1.getDatabase)();
|
|
18
18
|
const { searchParams } = new URL(request.url);
|
|
19
19
|
const providerId = searchParams.get('provider_id');
|
|
20
20
|
const enabledOnly = searchParams.get('enabled');
|
|
@@ -43,7 +43,7 @@ async function GET(request) {
|
|
|
43
43
|
}
|
|
44
44
|
async function POST(request) {
|
|
45
45
|
try {
|
|
46
|
-
(0, database_1.getDatabase)();
|
|
46
|
+
await (0, database_1.getDatabase)();
|
|
47
47
|
const body = await request.json();
|
|
48
48
|
const { provider_id, name, model_id, enabled } = body;
|
|
49
49
|
if (!provider_id || !name || !model_id) {
|
|
@@ -64,7 +64,7 @@ async function POST(request) {
|
|
|
64
64
|
}
|
|
65
65
|
async function PUT(request) {
|
|
66
66
|
try {
|
|
67
|
-
(0, database_1.getDatabase)();
|
|
67
|
+
await (0, database_1.getDatabase)();
|
|
68
68
|
const body = await request.json();
|
|
69
69
|
const { id, name, model_id, enabled, provider_id } = body;
|
|
70
70
|
if (!id) {
|
|
@@ -92,7 +92,7 @@ async function PUT(request) {
|
|
|
92
92
|
}
|
|
93
93
|
async function DELETE(request) {
|
|
94
94
|
try {
|
|
95
|
-
(0, database_1.getDatabase)();
|
|
95
|
+
await (0, database_1.getDatabase)();
|
|
96
96
|
const { searchParams } = new URL(request.url);
|
|
97
97
|
const id = searchParams.get('id');
|
|
98
98
|
if (!id) {
|
|
@@ -112,7 +112,7 @@ async function DELETE(request) {
|
|
|
112
112
|
// Fetch models from provider
|
|
113
113
|
async function PATCH(request) {
|
|
114
114
|
try {
|
|
115
|
-
(0, database_1.getDatabase)();
|
|
115
|
+
await (0, database_1.getDatabase)();
|
|
116
116
|
const body = await request.json();
|
|
117
117
|
const { provider_id } = body;
|
|
118
118
|
if (!provider_id) {
|
|
@@ -13,7 +13,7 @@ const crypto_1 = require("@/server/crypto");
|
|
|
13
13
|
exports.runtime = 'nodejs';
|
|
14
14
|
async function GET(request) {
|
|
15
15
|
try {
|
|
16
|
-
(0, database_1.getDatabase)();
|
|
16
|
+
await (0, database_1.getDatabase)();
|
|
17
17
|
const { searchParams } = new URL(request.url);
|
|
18
18
|
const id = searchParams.get('id');
|
|
19
19
|
const includeKey = searchParams.get('includeKey') === 'true';
|
|
@@ -45,7 +45,7 @@ async function GET(request) {
|
|
|
45
45
|
}
|
|
46
46
|
async function POST(request) {
|
|
47
47
|
try {
|
|
48
|
-
(0, database_1.getDatabase)();
|
|
48
|
+
await (0, database_1.getDatabase)();
|
|
49
49
|
const body = await request.json();
|
|
50
50
|
const { name, protocol, base_url, api_key } = body;
|
|
51
51
|
if (!name || !protocol || !base_url || !api_key) {
|
|
@@ -70,7 +70,7 @@ async function POST(request) {
|
|
|
70
70
|
}
|
|
71
71
|
async function PUT(request) {
|
|
72
72
|
try {
|
|
73
|
-
(0, database_1.getDatabase)();
|
|
73
|
+
await (0, database_1.getDatabase)();
|
|
74
74
|
const body = await request.json();
|
|
75
75
|
const { id, name, protocol, base_url, api_key } = body;
|
|
76
76
|
if (!id) {
|
|
@@ -106,7 +106,7 @@ async function PUT(request) {
|
|
|
106
106
|
}
|
|
107
107
|
async function DELETE(request) {
|
|
108
108
|
try {
|
|
109
|
-
(0, database_1.getDatabase)();
|
|
109
|
+
await (0, database_1.getDatabase)();
|
|
110
110
|
const { searchParams } = new URL(request.url);
|
|
111
111
|
const id = searchParams.get('id');
|
|
112
112
|
if (!id) {
|
|
@@ -10,7 +10,7 @@ const providers_1 = require("@/server/providers");
|
|
|
10
10
|
exports.runtime = 'nodejs';
|
|
11
11
|
async function POST(request) {
|
|
12
12
|
try {
|
|
13
|
-
(0, database_1.getDatabase)();
|
|
13
|
+
await (0, database_1.getDatabase)();
|
|
14
14
|
const body = await request.json();
|
|
15
15
|
const { provider_id, model_id } = body;
|
|
16
16
|
if (!provider_id || !model_id) {
|
|
@@ -15,7 +15,7 @@ exports.runtime = 'nodejs';
|
|
|
15
15
|
async function POST(request) {
|
|
16
16
|
try {
|
|
17
17
|
// Initialize database
|
|
18
|
-
(0, database_1.getDatabase)();
|
|
18
|
+
await (0, database_1.getDatabase)();
|
|
19
19
|
const body = await request.json().catch(() => ({}));
|
|
20
20
|
const port = body.port ? parseInt(body.port, 10) : null;
|
|
21
21
|
// Get port from config if not provided
|
|
@@ -10,7 +10,7 @@ exports.runtime = 'nodejs';
|
|
|
10
10
|
async function GET(request) {
|
|
11
11
|
try {
|
|
12
12
|
// Initialize database
|
|
13
|
-
(0, database_1.getDatabase)();
|
|
13
|
+
await (0, database_1.getDatabase)();
|
|
14
14
|
const status = await service_manager_1.serviceManager.getStatus();
|
|
15
15
|
return server_1.NextResponse.json(status);
|
|
16
16
|
}
|
|
@@ -10,7 +10,7 @@ exports.runtime = 'nodejs';
|
|
|
10
10
|
async function POST(request) {
|
|
11
11
|
try {
|
|
12
12
|
// Initialize database
|
|
13
|
-
(0, database_1.getDatabase)();
|
|
13
|
+
await (0, database_1.getDatabase)();
|
|
14
14
|
const result = await service_manager_1.serviceManager.stop();
|
|
15
15
|
if (result.error) {
|
|
16
16
|
return server_1.NextResponse.json(result, { status: 400 });
|
|
@@ -10,6 +10,18 @@ const react_1 = require("react");
|
|
|
10
10
|
const Nav_1 = __importDefault(require("../components/Nav"));
|
|
11
11
|
const ToastProvider_1 = require("../components/ToastProvider");
|
|
12
12
|
const ConfirmDialog_1 = __importDefault(require("../components/ConfirmDialog"));
|
|
13
|
+
/** 将服务端返回的 created_at 格式化为本地显示。无时区后缀时按 UTC 解析再转本地,避免差 8 小时。 */
|
|
14
|
+
function formatCreatedAt(createdAt) {
|
|
15
|
+
if (!createdAt || typeof createdAt !== 'string')
|
|
16
|
+
return '';
|
|
17
|
+
const s = createdAt.trim();
|
|
18
|
+
if (!s)
|
|
19
|
+
return '';
|
|
20
|
+
const hasTz = /[Z+-]\d{2}:?\d{2}$/.test(s);
|
|
21
|
+
const iso = hasTz ? s : s.replace(' ', 'T') + 'Z';
|
|
22
|
+
const date = new Date(iso);
|
|
23
|
+
return Number.isNaN(date.getTime()) ? s : date.toLocaleString('zh-CN');
|
|
24
|
+
}
|
|
13
25
|
function LogsPage() {
|
|
14
26
|
const [logs, setLogs] = (0, react_1.useState)([]);
|
|
15
27
|
const [selectedLog, setSelectedLog] = (0, react_1.useState)(null);
|
|
@@ -244,7 +256,7 @@ function LogsPage() {
|
|
|
244
256
|
}, className: "mt-2 text-xs text-emerald-600 hover:text-emerald-700", children: "\u6E05\u9664\u641C\u7D22" }))] }) }) })) : (logs.map((log) => {
|
|
245
257
|
const parsedModel = parseModelFromRequest(log.request_query, log.request_body);
|
|
246
258
|
const displayModel = parsedModel || log.model_name || 'Gateway';
|
|
247
|
-
return ((0, jsx_runtime_1.jsxs)("tr", { className: "hover:bg-emerald-50/20 transition-colors duration-300", children: [(0, jsx_runtime_1.jsx)("td", { className: "px-4 py-3", children: (0, jsx_runtime_1.jsx)("input", { type: "checkbox", checked: selectedIds.includes(log.id), onChange: (e) => handleSelectLog(log.id, e.target.checked), className: "w-4 h-4 rounded border-slate-300 text-emerald-600 focus:ring-emerald-500", onClick: (e) => e.stopPropagation() }) }), (0, jsx_runtime_1.jsx)("td", { className: "px-4 py-3 whitespace-nowrap", children: (0, jsx_runtime_1.jsx)("div", { className: "text-xs text-slate-600", children:
|
|
259
|
+
return ((0, jsx_runtime_1.jsxs)("tr", { className: "hover:bg-emerald-50/20 transition-colors duration-300", children: [(0, jsx_runtime_1.jsx)("td", { className: "px-4 py-3", children: (0, jsx_runtime_1.jsx)("input", { type: "checkbox", checked: selectedIds.includes(log.id), onChange: (e) => handleSelectLog(log.id, e.target.checked), className: "w-4 h-4 rounded border-slate-300 text-emerald-600 focus:ring-emerald-500", onClick: (e) => e.stopPropagation() }) }), (0, jsx_runtime_1.jsx)("td", { className: "px-4 py-3 whitespace-nowrap", children: (0, jsx_runtime_1.jsx)("div", { className: "text-xs text-slate-600", children: formatCreatedAt(log.created_at) }) }), (0, jsx_runtime_1.jsx)("td", { className: "px-4 py-3 whitespace-nowrap", children: (0, jsx_runtime_1.jsxs)("div", { className: "text-xs", children: [(0, jsx_runtime_1.jsx)("div", { className: "font-medium text-slate-800", children: displayModel }), log.provider_name && ((0, jsx_runtime_1.jsx)("div", { className: "text-[10px] text-slate-500", children: log.provider_name }))] }) }), (0, jsx_runtime_1.jsx)("td", { className: "px-4 py-3 whitespace-nowrap", children: (0, jsx_runtime_1.jsx)("span", { className: `px-2 py-0.5 inline-flex text-xs leading-4 font-semibold rounded-lg border ${log.request_method === 'GET' ? 'bg-sky-50 text-sky-700 border-sky-200/50' :
|
|
248
260
|
log.request_method === 'POST' ? 'bg-emerald-50 text-emerald-700 border-emerald-200/50' :
|
|
249
261
|
'bg-slate-50 text-slate-600 border-slate-200/50'}`, children: log.request_method }) }), (0, jsx_runtime_1.jsx)("td", { className: "px-4 py-3", children: (0, jsx_runtime_1.jsx)("div", { className: "text-xs text-slate-600 font-mono max-w-xs truncate", children: log.request_path }) }), (0, jsx_runtime_1.jsx)("td", { className: "px-4 py-3 whitespace-nowrap", children: (0, jsx_runtime_1.jsx)("span", { className: `px-2 py-0.5 inline-flex text-xs leading-4 font-semibold rounded-full border ${log.response_status >= 200 && log.response_status < 300 ? 'bg-emerald-100/80 text-emerald-700 border-emerald-200/50' :
|
|
250
262
|
log.response_status >= 400 && log.response_status < 500 ? 'bg-amber-100/80 text-amber-700 border-amber-200/50' :
|