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.
Files changed (133) hide show
  1. package/.next/BUILD_ID +1 -1
  2. package/.next/build-manifest.json +2 -2
  3. package/.next/fallback-build-manifest.json +2 -2
  4. package/.next/server/app/_global-error.html +2 -2
  5. package/.next/server/app/_global-error.rsc +1 -1
  6. package/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  7. package/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  8. package/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  9. package/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  10. package/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  11. package/.next/server/app/_not-found.html +1 -1
  12. package/.next/server/app/_not-found.rsc +1 -1
  13. package/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  14. package/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  15. package/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  16. package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  17. package/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  18. package/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  19. package/.next/server/app/api/config/route.js.nft.json +1 -1
  20. package/.next/server/app/api/gateway/[...path]/route.js.nft.json +1 -1
  21. package/.next/server/app/api/gateway/models/route.js.nft.json +1 -1
  22. package/.next/server/app/api/gateway/route.js.nft.json +1 -1
  23. package/.next/server/app/api/ide/claude/apply/route.js.nft.json +1 -1
  24. package/.next/server/app/api/ide/claude/available-models/route.js.nft.json +1 -1
  25. package/.next/server/app/api/ide/claude/save/route.js.nft.json +1 -1
  26. package/.next/server/app/api/ide/claude/status/route.js.nft.json +1 -1
  27. package/.next/server/app/api/ide/claude/test/route.js.nft.json +1 -1
  28. package/.next/server/app/api/logs/route.js.nft.json +1 -1
  29. package/.next/server/app/api/models/route.js.nft.json +1 -1
  30. package/.next/server/app/api/providers/route.js.nft.json +1 -1
  31. package/.next/server/app/api/providers/test/route.js.nft.json +1 -1
  32. package/.next/server/app/api/service/force-stop/route.js.nft.json +1 -1
  33. package/.next/server/app/api/service/start/route.js.nft.json +1 -1
  34. package/.next/server/app/api/service/status/route.js.nft.json +1 -1
  35. package/.next/server/app/api/service/stop/route.js.nft.json +1 -1
  36. package/.next/server/app/ide.html +1 -1
  37. package/.next/server/app/ide.rsc +1 -1
  38. package/.next/server/app/ide.segments/_full.segment.rsc +1 -1
  39. package/.next/server/app/ide.segments/_head.segment.rsc +1 -1
  40. package/.next/server/app/ide.segments/_index.segment.rsc +1 -1
  41. package/.next/server/app/ide.segments/_tree.segment.rsc +1 -1
  42. package/.next/server/app/ide.segments/ide/__PAGE__.segment.rsc +1 -1
  43. package/.next/server/app/ide.segments/ide.segment.rsc +1 -1
  44. package/.next/server/app/index.html +1 -1
  45. package/.next/server/app/index.rsc +1 -1
  46. package/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  47. package/.next/server/app/index.segments/_full.segment.rsc +1 -1
  48. package/.next/server/app/index.segments/_head.segment.rsc +1 -1
  49. package/.next/server/app/index.segments/_index.segment.rsc +1 -1
  50. package/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  51. package/.next/server/app/logs/page_client-reference-manifest.js +1 -1
  52. package/.next/server/app/logs.html +1 -1
  53. package/.next/server/app/logs.rsc +2 -2
  54. package/.next/server/app/logs.segments/_full.segment.rsc +2 -2
  55. package/.next/server/app/logs.segments/_head.segment.rsc +1 -1
  56. package/.next/server/app/logs.segments/_index.segment.rsc +1 -1
  57. package/.next/server/app/logs.segments/_tree.segment.rsc +1 -1
  58. package/.next/server/app/logs.segments/logs/__PAGE__.segment.rsc +2 -2
  59. package/.next/server/app/logs.segments/logs.segment.rsc +1 -1
  60. package/.next/server/app/models.html +1 -1
  61. package/.next/server/app/models.rsc +1 -1
  62. package/.next/server/app/models.segments/_full.segment.rsc +1 -1
  63. package/.next/server/app/models.segments/_head.segment.rsc +1 -1
  64. package/.next/server/app/models.segments/_index.segment.rsc +1 -1
  65. package/.next/server/app/models.segments/_tree.segment.rsc +1 -1
  66. package/.next/server/app/models.segments/models/__PAGE__.segment.rsc +1 -1
  67. package/.next/server/app/models.segments/models.segment.rsc +1 -1
  68. package/.next/server/app/providers.html +1 -1
  69. package/.next/server/app/providers.rsc +1 -1
  70. package/.next/server/app/providers.segments/_full.segment.rsc +1 -1
  71. package/.next/server/app/providers.segments/_head.segment.rsc +1 -1
  72. package/.next/server/app/providers.segments/_index.segment.rsc +1 -1
  73. package/.next/server/app/providers.segments/_tree.segment.rsc +1 -1
  74. package/.next/server/app/providers.segments/providers/__PAGE__.segment.rsc +1 -1
  75. package/.next/server/app/providers.segments/providers.segment.rsc +1 -1
  76. package/.next/server/chunks/[root-of-the-server]__1480f018._.js +1 -1
  77. package/.next/server/chunks/[root-of-the-server]__1480f018._.js.map +1 -1
  78. package/.next/server/chunks/[root-of-the-server]__1909f3aa._.js +1 -1
  79. package/.next/server/chunks/[root-of-the-server]__1909f3aa._.js.map +1 -1
  80. package/.next/server/chunks/[root-of-the-server]__1d4b7fc5._.js +1 -1
  81. package/.next/server/chunks/[root-of-the-server]__1d4b7fc5._.js.map +1 -1
  82. package/.next/server/chunks/[root-of-the-server]__372ef2bf._.js +1 -1
  83. package/.next/server/chunks/[root-of-the-server]__372ef2bf._.js.map +1 -1
  84. package/.next/server/chunks/[root-of-the-server]__3aaf963c._.js +1 -1
  85. package/.next/server/chunks/[root-of-the-server]__3aaf963c._.js.map +1 -1
  86. package/.next/server/chunks/[root-of-the-server]__6ce199d2._.js +1 -1
  87. package/.next/server/chunks/[root-of-the-server]__6ce199d2._.js.map +1 -1
  88. package/.next/server/chunks/[root-of-the-server]__772134c6._.js +1 -1
  89. package/.next/server/chunks/[root-of-the-server]__772134c6._.js.map +1 -1
  90. package/.next/server/chunks/[root-of-the-server]__7b77f523._.js +1 -1
  91. package/.next/server/chunks/[root-of-the-server]__7b77f523._.js.map +1 -1
  92. package/.next/server/chunks/[root-of-the-server]__c1b4b601._.js +18 -18
  93. package/.next/server/chunks/[root-of-the-server]__c1b4b601._.js.map +1 -1
  94. package/.next/server/chunks/[root-of-the-server]__ccfc7f1d._.js +1 -1
  95. package/.next/server/chunks/[root-of-the-server]__ccfc7f1d._.js.map +1 -1
  96. package/.next/server/chunks/ssr/src_app_logs_page_tsx_7b7b7b83._.js +1 -1
  97. package/.next/server/chunks/ssr/src_app_logs_page_tsx_7b7b7b83._.js.map +1 -1
  98. package/.next/server/pages/404.html +1 -1
  99. package/.next/server/pages/500.html +2 -2
  100. package/.next/static/chunks/{81c904164fe81379.js → b6b258e8582e47c4.js} +1 -1
  101. package/README.md +100 -111
  102. package/dist/src/app/api/gateway/[...path]/route.js +1 -1
  103. package/dist/src/app/api/gateway/route.js +1 -1
  104. package/dist/src/app/api/logs/route.js +2 -2
  105. package/dist/src/app/api/models/route.js +5 -5
  106. package/dist/src/app/api/providers/route.js +4 -4
  107. package/dist/src/app/api/providers/test/route.js +1 -1
  108. package/dist/src/app/api/service/start/route.js +1 -1
  109. package/dist/src/app/api/service/status/route.js +1 -1
  110. package/dist/src/app/api/service/stop/route.js +1 -1
  111. package/dist/src/app/logs/page.js +13 -1
  112. package/dist/src/cli/index.js +218 -20
  113. package/dist/src/db/database.js +35 -1
  114. package/dist/src/db/queries.js +6 -6
  115. package/dist/src/server/logger.js +22 -4
  116. package/package.json +2 -1
  117. package/src/app/api/gateway/[...path]/route.ts +1 -1
  118. package/src/app/api/gateway/route.ts +1 -1
  119. package/src/app/api/logs/route.ts +2 -2
  120. package/src/app/api/models/route.ts +5 -5
  121. package/src/app/api/providers/route.ts +4 -4
  122. package/src/app/api/providers/test/route.ts +1 -1
  123. package/src/app/api/service/start/route.ts +1 -1
  124. package/src/app/api/service/status/route.ts +1 -1
  125. package/src/app/api/service/stop/route.ts +1 -1
  126. package/src/app/logs/page.tsx +15 -5
  127. package/src/cli/index.ts +228 -25
  128. package/src/db/database.ts +34 -4
  129. package/src/db/queries.ts +6 -6
  130. package/src/server/logger.ts +19 -4
  131. /package/.next/static/{PkfqdzwOZgX-UhSNUuhdp → ryTeHAYUvjT1bYolc-x9Z}/_buildManifest.js +0 -0
  132. /package/.next/static/{PkfqdzwOZgX-UhSNUuhdp → ryTeHAYUvjT1bYolc-x9Z}/_clientMiddlewareManifest.json +0 -0
  133. /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、Zcode、Alma 等客户端软件提供统一的接口。
3
+ 一个统一的 API 网关,用于管理多个 AI 模型供应商(Anthropic、OpenAI、Gemini 等),为 Claude、Cursor、Alma 等客户端提供统一接口。
4
4
 
5
5
  ## 功能特性
6
6
 
7
7
  - ✅ 支持多种 AI 模型协议(OpenAI、Anthropic、Gemini)
8
- - ✅ 独立 Gateway 服务架构,可与 Web UI 分离运行
8
+ - ✅ 一键启动:前台 Web UI + 网关服务,默认后台运行
9
9
  - ✅ Web 管理界面,可视化配置和管理
10
10
  - ✅ 模型供应商管理(添加、编辑、删除)
11
11
  - ✅ 模型管理(手动添加、自动拉取模型列表)
12
12
  - ✅ 请求日志记录和查看(支持流式响应的美化展示)
13
13
  - ✅ 双端点支持(`/v1/*` 和 `/api/gateway/*`)
14
- - ✅ API Key 认证保护 Gateway 服务
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
- ### 启动完整服务(Web UI + Gateway)
28
+ ### 启动服务(默认:后台运行 Web UI + 网关)
27
29
 
28
30
  ```bash
29
31
  aar start
30
32
  ```
31
33
 
32
- 默认在 `http://localhost:9527` 启动 Web 管理界面,Gateway 服务运行在 `http://localhost:1357`。
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
- ### 仅启动 Gateway 服务
62
+ 若希望在前台运行、用 Ctrl+C 结束:
35
63
 
36
64
  ```bash
37
- aar gateway
65
+ aar start --no-background
38
66
  ```
39
67
 
40
- 仅启动独立的 API 网关服务,默认在 `http://localhost:1357`。
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 --port 8080
79
+ # 指定 Web UI 端口(默认 9527)
80
+ aar start -p 8080
47
81
 
48
- # 指定主机名
49
- aar start --hostname 0.0.0.0
82
+ # 指定网关端口(默认从配置或 1357)
83
+ aar start -g 3000
50
84
 
51
- # 指定 Gateway 端口
52
- aar gateway --port 3000
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 8080
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
- 启动后访问 `http://localhost:9527`,你可以:
104
+ 启动后访问 **http://localhost:9527**,可以:
71
105
 
72
- 1. **配置网关**:设置端口、API Key 等
106
+ 1. **配置网关**:端口、API Key 等
73
107
  2. **管理供应商**:添加、编辑、删除模型供应商
74
- 3. **管理模型**:手动添加模型或一键拉取模型列表
75
- 4. **查看日志**:查看所有 API 请求日志,包括流式响应的美化显示
76
- 5. **服务管理**:独立启动/停止 Gateway 服务,不影响 Web UI
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
- 1. 进入"模型"页面
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
- 1. 进入"模型"页面
99
- 2. 在"一键拉取模型列表"区域,点击对应供应商的"拉取模型"按钮
100
- 3. 系统会自动从供应商 API 拉取可用模型列表
118
+ ## 使用网关 API
101
119
 
102
- ## 使用网关
120
+ 配置好供应商和模型后,客户端可请求网关(默认 `http://localhost:1357`)。
103
121
 
104
- 配置好供应商和模型后,客户端可以通过以下方式使用:
122
+ ### OpenAI 兼容
105
123
 
106
- ### Gateway 端点(推荐 - 独立服务)
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
- x-api-key: your-gateway-api-key
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
- #### Gateway 专用接口
136
+ ### 网关路径形式
125
137
 
126
- ```
138
+ ```http
127
139
  POST http://localhost:1357/api/gateway/v1/chat/completions
128
- Content-Type: application/json
129
- x-api-key: your-gateway-api-key
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
- aar/
185
- ├── src/
186
- │ ├── server/ # 网关服务器
187
- ├── gateway.ts # 核心网关逻辑
188
- ├── gateway-server.ts # 独立 Gateway 服务器
189
- ├── service-manager.ts # 服务管理器
190
- │ ├── providers/ # 协议适配器
191
- │ │ └── logger.ts # 请求日志记录
192
- │ ├── db/ # 数据库层
193
- ├── database.ts # 数据库连接
194
- │ ├── schema.ts # SQLite 表结构
195
- │ │ └── queries.ts # 数据库查询
196
- │ ├── app/ # Next.js 应用
197
- │ ├── api/ # API 路由
198
- │ │ │ ├── gateway/ # Gateway 代理端点
199
- │ │ │ └── service/ # 服务管理端点
200
- │ │ └── (pages)/ # 前端页面
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 14** - 全栈框架
199
+ - **Next.js** - 全栈与 Web UI
211
200
  - **TypeScript** - 类型安全
212
- - **Tailwind CSS** - 样式框架
213
- - **SQLite3** - 数据库
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 Pull Request
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: new Date(log.created_at + 'Z').toLocaleString('zh-CN') }) }), (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' :
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' :