ai-agent-router 0.1.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 (166) hide show
  1. package/.claude/commands/openspec/apply.md +23 -0
  2. package/.claude/commands/openspec/archive.md +27 -0
  3. package/.claude/commands/openspec/proposal.md +28 -0
  4. package/.claude/settings.local.json +12 -0
  5. package/.claude/skills/ui-ux-pro-max/SKILL.md +228 -0
  6. package/.claude/skills/ui-ux-pro-max/data/charts.csv +26 -0
  7. package/.claude/skills/ui-ux-pro-max/data/colors.csv +97 -0
  8. package/.claude/skills/ui-ux-pro-max/data/landing.csv +31 -0
  9. package/.claude/skills/ui-ux-pro-max/data/products.csv +97 -0
  10. package/.claude/skills/ui-ux-pro-max/data/prompts.csv +24 -0
  11. package/.claude/skills/ui-ux-pro-max/data/stacks/flutter.csv +53 -0
  12. package/.claude/skills/ui-ux-pro-max/data/stacks/html-tailwind.csv +56 -0
  13. package/.claude/skills/ui-ux-pro-max/data/stacks/nextjs.csv +53 -0
  14. package/.claude/skills/ui-ux-pro-max/data/stacks/nuxt-ui.csv +51 -0
  15. package/.claude/skills/ui-ux-pro-max/data/stacks/nuxtjs.csv +59 -0
  16. package/.claude/skills/ui-ux-pro-max/data/stacks/react-native.csv +52 -0
  17. package/.claude/skills/ui-ux-pro-max/data/stacks/react.csv +54 -0
  18. package/.claude/skills/ui-ux-pro-max/data/stacks/svelte.csv +54 -0
  19. package/.claude/skills/ui-ux-pro-max/data/stacks/swiftui.csv +51 -0
  20. package/.claude/skills/ui-ux-pro-max/data/stacks/vue.csv +50 -0
  21. package/.claude/skills/ui-ux-pro-max/data/styles.csv +59 -0
  22. package/.claude/skills/ui-ux-pro-max/data/typography.csv +58 -0
  23. package/.claude/skills/ui-ux-pro-max/data/ux-guidelines.csv +100 -0
  24. package/.claude/skills/ui-ux-pro-max/scripts/__pycache__/core.cpython-311.pyc +0 -0
  25. package/.claude/skills/ui-ux-pro-max/scripts/core.py +238 -0
  26. package/.claude/skills/ui-ux-pro-max/scripts/search.py +61 -0
  27. package/.cursor/commands/openspec-apply.md +23 -0
  28. package/.cursor/commands/openspec-archive.md +27 -0
  29. package/.cursor/commands/openspec-proposal.md +28 -0
  30. package/.cursor/commands/ui-ux-pro-max.md +226 -0
  31. package/.eslintrc.json +3 -0
  32. package/.shared/ui-ux-pro-max/data/charts.csv +26 -0
  33. package/.shared/ui-ux-pro-max/data/colors.csv +97 -0
  34. package/.shared/ui-ux-pro-max/data/landing.csv +31 -0
  35. package/.shared/ui-ux-pro-max/data/products.csv +97 -0
  36. package/.shared/ui-ux-pro-max/data/prompts.csv +24 -0
  37. package/.shared/ui-ux-pro-max/data/stacks/flutter.csv +53 -0
  38. package/.shared/ui-ux-pro-max/data/stacks/html-tailwind.csv +56 -0
  39. package/.shared/ui-ux-pro-max/data/stacks/nextjs.csv +53 -0
  40. package/.shared/ui-ux-pro-max/data/stacks/nuxt-ui.csv +51 -0
  41. package/.shared/ui-ux-pro-max/data/stacks/nuxtjs.csv +59 -0
  42. package/.shared/ui-ux-pro-max/data/stacks/react-native.csv +52 -0
  43. package/.shared/ui-ux-pro-max/data/stacks/react.csv +54 -0
  44. package/.shared/ui-ux-pro-max/data/stacks/svelte.csv +54 -0
  45. package/.shared/ui-ux-pro-max/data/stacks/swiftui.csv +51 -0
  46. package/.shared/ui-ux-pro-max/data/stacks/vue.csv +50 -0
  47. package/.shared/ui-ux-pro-max/data/styles.csv +59 -0
  48. package/.shared/ui-ux-pro-max/data/typography.csv +58 -0
  49. package/.shared/ui-ux-pro-max/data/ux-guidelines.csv +100 -0
  50. package/.shared/ui-ux-pro-max/scripts/core.py +238 -0
  51. package/.shared/ui-ux-pro-max/scripts/search.py +61 -0
  52. package/AGENTS.md +18 -0
  53. package/CLAUDE.md +18 -0
  54. package/IMPLEMENTATION.md +157 -0
  55. package/LICENSE +21 -0
  56. package/README.md +165 -0
  57. package/dist/.next/types/app/api/config/route.js +52 -0
  58. package/dist/.next/types/app/api/gateway/[...path]/route.js +52 -0
  59. package/dist/.next/types/app/api/gateway/route.js +52 -0
  60. package/dist/.next/types/app/api/logs/route.js +52 -0
  61. package/dist/.next/types/app/api/models/route.js +52 -0
  62. package/dist/.next/types/app/api/providers/route.js +52 -0
  63. package/dist/.next/types/app/api/providers/test/route.js +52 -0
  64. package/dist/.next/types/app/api/service/start/route.js +52 -0
  65. package/dist/.next/types/app/api/service/status/route.js +52 -0
  66. package/dist/.next/types/app/api/service/stop/route.js +52 -0
  67. package/dist/.next/types/app/layout.js +22 -0
  68. package/dist/.next/types/app/logs/page.js +22 -0
  69. package/dist/.next/types/app/models/page.js +22 -0
  70. package/dist/.next/types/app/page.js +22 -0
  71. package/dist/.next/types/app/providers/page.js +22 -0
  72. package/dist/src/app/api/config/route.js +43 -0
  73. package/dist/src/app/api/gateway/[...path]/route.js +83 -0
  74. package/dist/src/app/api/gateway/route.js +63 -0
  75. package/dist/src/app/api/logs/route.js +34 -0
  76. package/dist/src/app/api/models/route.js +152 -0
  77. package/dist/src/app/api/providers/route.js +118 -0
  78. package/dist/src/app/api/providers/test/route.js +154 -0
  79. package/dist/src/app/api/service/start/route.js +55 -0
  80. package/dist/src/app/api/service/status/route.js +17 -0
  81. package/dist/src/app/api/service/stop/route.js +20 -0
  82. package/dist/src/app/components/ConfirmDialog.jsx +31 -0
  83. package/dist/src/app/components/Nav.jsx +45 -0
  84. package/dist/src/app/components/Toast.jsx +37 -0
  85. package/dist/src/app/components/ToastProvider.jsx +21 -0
  86. package/dist/src/app/layout.jsx +13 -0
  87. package/dist/src/app/logs/page.jsx +210 -0
  88. package/dist/src/app/models/page.jsx +291 -0
  89. package/dist/src/app/page.jsx +236 -0
  90. package/dist/src/app/providers/page.jsx +402 -0
  91. package/dist/src/cli/index.js +90 -0
  92. package/dist/src/db/database.js +69 -0
  93. package/dist/src/db/queries.js +261 -0
  94. package/dist/src/db/schema.js +67 -0
  95. package/dist/src/server/crypto.js +22 -0
  96. package/dist/src/server/gateway-server.js +200 -0
  97. package/dist/src/server/gateway.js +76 -0
  98. package/dist/src/server/logger.js +72 -0
  99. package/dist/src/server/providers/anthropic.js +52 -0
  100. package/dist/src/server/providers/gemini.js +64 -0
  101. package/dist/src/server/providers/index.js +16 -0
  102. package/dist/src/server/providers/openai.js +86 -0
  103. package/dist/src/server/providers/types.js +1 -0
  104. package/dist/src/server/service-manager.js +286 -0
  105. package/docs/TODO.md +19 -0
  106. package/next.config.js +7 -0
  107. package/openspec/AGENTS.md +456 -0
  108. package/openspec/changes/add-logging/proposal.md +18 -0
  109. package/openspec/changes/add-logging/specs/core/spec.md +21 -0
  110. package/openspec/changes/add-logging/tasks.md +16 -0
  111. package/openspec/changes/add-provider-test-connection/proposal.md +22 -0
  112. package/openspec/changes/add-provider-test-connection/specs/model-provider/spec.md +68 -0
  113. package/openspec/changes/add-provider-test-connection/tasks.md +31 -0
  114. package/openspec/changes/improve-gateway-startup/design.md +137 -0
  115. package/openspec/changes/improve-gateway-startup/proposal.md +33 -0
  116. package/openspec/changes/improve-gateway-startup/specs/api-gateway/spec.md +94 -0
  117. package/openspec/changes/improve-gateway-startup/specs/web-ui/spec.md +67 -0
  118. package/openspec/changes/improve-gateway-startup/tasks.md +47 -0
  119. package/openspec/changes/init-api-gateway/design.md +185 -0
  120. package/openspec/changes/init-api-gateway/proposal.md +30 -0
  121. package/openspec/changes/init-api-gateway/specs/api-gateway/spec.md +42 -0
  122. package/openspec/changes/init-api-gateway/specs/cli-tool/spec.md +40 -0
  123. package/openspec/changes/init-api-gateway/specs/model-management/spec.md +47 -0
  124. package/openspec/changes/init-api-gateway/specs/model-provider/spec.md +33 -0
  125. package/openspec/changes/init-api-gateway/specs/request-logging/spec.md +54 -0
  126. package/openspec/changes/init-api-gateway/specs/web-ui/spec.md +49 -0
  127. package/openspec/changes/init-api-gateway/tasks.md +84 -0
  128. package/openspec/project.md +58 -0
  129. package/package.json +51 -0
  130. package/postcss.config.js +6 -0
  131. package/src/app/api/config/route.ts +62 -0
  132. package/src/app/api/gateway/[...path]/route.ts +118 -0
  133. package/src/app/api/gateway/route.ts +77 -0
  134. package/src/app/api/logs/route.ts +48 -0
  135. package/src/app/api/models/route.ts +210 -0
  136. package/src/app/api/providers/route.ts +162 -0
  137. package/src/app/api/providers/test/route.ts +182 -0
  138. package/src/app/api/service/start/route.ts +73 -0
  139. package/src/app/api/service/status/route.ts +22 -0
  140. package/src/app/api/service/stop/route.ts +27 -0
  141. package/src/app/components/ConfirmDialog.tsx +63 -0
  142. package/src/app/components/Nav.tsx +66 -0
  143. package/src/app/components/Toast.tsx +61 -0
  144. package/src/app/components/ToastProvider.tsx +43 -0
  145. package/src/app/globals.css +71 -0
  146. package/src/app/layout.tsx +22 -0
  147. package/src/app/logs/page.tsx +261 -0
  148. package/src/app/models/page.tsx +500 -0
  149. package/src/app/page.tsx +742 -0
  150. package/src/app/providers/page.tsx +558 -0
  151. package/src/cli/index.ts +95 -0
  152. package/src/db/database.ts +125 -0
  153. package/src/db/queries.ts +339 -0
  154. package/src/db/schema.ts +117 -0
  155. package/src/server/crypto.ts +48 -0
  156. package/src/server/gateway-server.ts +306 -0
  157. package/src/server/gateway.ts +163 -0
  158. package/src/server/logger.ts +96 -0
  159. package/src/server/providers/anthropic.ts +121 -0
  160. package/src/server/providers/gemini.ts +112 -0
  161. package/src/server/providers/index.ts +20 -0
  162. package/src/server/providers/openai.ts +235 -0
  163. package/src/server/providers/types.ts +20 -0
  164. package/src/server/service-manager.ts +321 -0
  165. package/tailwind.config.js +16 -0
  166. package/tsconfig.json +29 -0
@@ -0,0 +1,47 @@
1
+ ## ADDED Requirements
2
+
3
+ ### Requirement: 模型管理
4
+ The system SHALL allow users to add and manage AI models for each provider.
5
+
6
+ #### Scenario: 手动添加模型
7
+ - **WHEN** 用户选择供应商并填写模型信息(名称、模型 ID)
8
+ - **THEN** 模型保存到数据库并关联到供应商
9
+ - **AND** 模型出现在模型列表中
10
+ - **AND** 模型默认启用状态
11
+
12
+ #### Scenario: 一键拉取模型列表
13
+ - **WHEN** 用户点击"拉取模型列表"按钮
14
+ - **THEN** 系统调用供应商 API 获取可用模型列表
15
+ - **AND** 新模型自动添加到数据库
16
+ - **AND** 已存在的模型更新信息
17
+ - **AND** 显示拉取结果(成功/失败,新增模型数量)
18
+
19
+ #### Scenario: 启用/禁用模型
20
+ - **WHEN** 用户切换模型的启用状态
21
+ - **THEN** 模型状态更新到数据库
22
+ - **AND** 禁用的模型不再接受请求路由
23
+
24
+ #### Scenario: 删除模型
25
+ - **WHEN** 用户删除模型
26
+ - **THEN** 模型从数据库删除
27
+ - **AND** 该模型不再可用
28
+
29
+ #### Scenario: 查看模型列表
30
+ - **WHEN** 用户访问模型管理页面
31
+ - **THEN** 显示所有供应商的模型列表
32
+ - **AND** 可以按供应商筛选
33
+ - **AND** 显示模型状态(启用/禁用)
34
+
35
+ ### Requirement: 模型自动拉取
36
+ The system SHALL support automatic fetching of model lists from provider APIs.
37
+
38
+ #### Scenario: 拉取 OpenAI 模型列表
39
+ - **WHEN** 用户为 OpenAI 供应商点击拉取按钮
40
+ - **THEN** 系统调用 OpenAI API 获取模型列表
41
+ - **AND** 解析返回的模型数据
42
+ - **AND** 更新或创建模型记录
43
+
44
+ #### Scenario: 拉取失败处理
45
+ - **WHEN** 拉取模型列表时 API 调用失败
46
+ - **THEN** 显示错误信息给用户
47
+ - **AND** 不影响已存在的模型
@@ -0,0 +1,33 @@
1
+ ## ADDED Requirements
2
+
3
+ ### Requirement: 模型供应商管理
4
+ The system SHALL allow users to add, configure, and manage multiple AI model providers.
5
+
6
+ #### Scenario: 添加新供应商
7
+ - **WHEN** 用户在 Web 界面填写供应商信息(名称、协议类型、baseUrl、API Key)
8
+ - **THEN** 供应商信息保存到数据库
9
+ - **AND** API Key 加密存储
10
+ - **AND** 供应商出现在供应商列表中
11
+
12
+ #### Scenario: 编辑供应商配置
13
+ - **WHEN** 用户修改供应商的 baseUrl 或 API Key
14
+ - **THEN** 更新后的配置保存到数据库
15
+ - **AND** 配置立即生效,无需重启网关
16
+
17
+ #### Scenario: 删除供应商
18
+ - **WHEN** 用户删除供应商
19
+ - **THEN** 供应商从数据库删除
20
+ - **AND** 关联的模型也被标记为不可用(或删除)
21
+
22
+ #### Scenario: 验证供应商配置
23
+ - **WHEN** 用户添加或编辑供应商
24
+ - **THEN** 系统验证 baseUrl 格式和 API Key 格式
25
+ - **AND** 如果配置无效,显示错误提示
26
+
27
+ ### Requirement: 供应商协议类型
28
+ The system SHALL support different provider protocol types.
29
+
30
+ #### Scenario: 选择协议类型
31
+ - **WHEN** 用户添加供应商
32
+ - **THEN** 可以从下拉列表选择协议类型(OpenAI、Anthropic、Gemini)
33
+ - **AND** 根据协议类型显示相应的配置字段
@@ -0,0 +1,54 @@
1
+ ## ADDED Requirements
2
+
3
+ ### Requirement: 请求日志记录
4
+ The system SHALL record all API requests passing through the gateway.
5
+
6
+ #### Scenario: 记录请求信息
7
+ - **WHEN** 客户端向网关发送请求
8
+ - **THEN** 请求信息记录到数据库
9
+ - **AND** 记录包括:请求方法、路径、请求头、query 参数、请求体
10
+ - **AND** 记录响应信息:状态码、响应体、响应时间
11
+
12
+ #### Scenario: 日志存储
13
+ - **WHEN** 请求处理完成
14
+ - **THEN** 日志立即写入数据库
15
+ - **AND** 日志包含时间戳和关联的模型信息
16
+ - **AND** 敏感信息(API Key、Token)自动脱敏
17
+
18
+ ### Requirement: 日志查看界面
19
+ The system SHALL provide a web interface to view and inspect request logs.
20
+
21
+ #### Scenario: 查看日志列表
22
+ - **WHEN** 用户访问日志页面
23
+ - **THEN** 显示请求日志列表(按时间倒序)
24
+ - **AND** 显示关键信息:时间、模型、状态码、响应时间
25
+ - **AND** 支持分页加载
26
+ - **AND** 支持按模型、状态码、时间范围筛选
27
+
28
+ #### Scenario: 查看日志详情
29
+ - **WHEN** 用户点击日志列表中的某条记录
30
+ - **THEN** 显示完整的请求和响应详情
31
+ - **AND** 请求头以 JSON 格式展示(美化)
32
+ - **AND** 请求 query 以 JSON 格式展示(美化)
33
+ - **AND** 请求 body 以 JSON 格式展示(美化)
34
+ - **AND** 响应 body 以 JSON 格式展示(美化)
35
+ - **AND** JSON 格式化,支持折叠展开
36
+
37
+ #### Scenario: JSON 美化展示
38
+ - **WHEN** 日志详情中的 JSON 数据渲染
39
+ - **THEN** JSON 格式化显示(缩进、语法高亮)
40
+ - **AND** 支持展开/折叠嵌套对象
41
+ - **AND** 长文本支持滚动查看
42
+
43
+ ### Requirement: 敏感信息脱敏
44
+ The system SHALL automatically mask sensitive information in logs.
45
+
46
+ #### Scenario: API Key 脱敏
47
+ - **WHEN** 请求头或请求体中包含 API Key
48
+ - **THEN** 日志中 API Key 被替换为 `***` 或部分隐藏
49
+ - **AND** 原始值不存储在日志中
50
+
51
+ #### Scenario: Token 脱敏
52
+ - **WHEN** 请求中包含 Authorization Token
53
+ - **THEN** Token 在日志中被脱敏处理
54
+ - **AND** 只显示 Token 类型和部分标识
@@ -0,0 +1,49 @@
1
+ ## ADDED Requirements
2
+
3
+ ### Requirement: Web 管理界面
4
+ The system SHALL provide a web-based management interface built with Next.js and Tailwind CSS.
5
+
6
+ #### Scenario: 访问管理界面
7
+ - **WHEN** 网关服务启动
8
+ - **THEN** Web 管理界面可通过浏览器访问(默认端口或配置的端口)
9
+ - **AND** 界面使用 Tailwind CSS 样式,美观现代
10
+
11
+ #### Scenario: 网关配置界面
12
+ - **WHEN** 用户访问主页面
13
+ - **THEN** 显示网关配置选项(端口、API_KEY)
14
+ - **AND** 可以修改配置并保存
15
+ - **AND** 配置保存后立即生效或提示重启
16
+
17
+ #### Scenario: 启动/停止网关
18
+ - **WHEN** 用户点击启动按钮
19
+ - **THEN** 网关服务启动
20
+ - **AND** 按钮状态变为"运行中"
21
+ - **AND** 显示网关运行状态和端口信息
22
+
23
+ #### Scenario: 供应商管理页面
24
+ - **WHEN** 用户访问供应商管理页面
25
+ - **THEN** 显示供应商列表
26
+ - **AND** 可以添加、编辑、删除供应商
27
+ - **AND** 界面响应式设计,支持移动端
28
+
29
+ #### Scenario: 模型管理页面
30
+ - **WHEN** 用户访问模型管理页面
31
+ - **THEN** 显示模型列表(按供应商分组)
32
+ - **AND** 可以手动添加模型
33
+ - **AND** 可以点击按钮拉取模型列表
34
+ - **AND** 可以启用/禁用、删除模型
35
+
36
+ #### Scenario: UI 优化
37
+ - **WHEN** 界面渲染
38
+ - **THEN** 使用 ui-ux-pro-max 进行 UI 优化
39
+ - **AND** 界面美观、易用、符合现代设计规范
40
+ - **AND** 交互流畅,反馈及时
41
+
42
+ ### Requirement: 响应式设计
43
+ The system SHALL provide responsive web interface that works on different screen sizes.
44
+
45
+ #### Scenario: 移动端访问
46
+ - **WHEN** 用户在移动设备上访问管理界面
47
+ - **THEN** 界面自适应屏幕大小
48
+ - **AND** 所有功能可用
49
+ - **AND** 布局合理,易于操作
@@ -0,0 +1,84 @@
1
+ ## 1. 项目初始化
2
+ - [ ] 1.1 创建项目目录结构
3
+ - [ ] 1.2 初始化 package.json,配置依赖(nextjs, tailwindcss, sqlite3, commander 等)
4
+ - [ ] 1.3 配置 TypeScript 和 Next.js
5
+ - [ ] 1.4 配置 Tailwind CSS
6
+ - [ ] 1.5 初始化 Git 仓库
7
+
8
+ ## 2. 数据库层
9
+ - [ ] 2.1 设计 SQLite 数据库 schema(providers, models, request_logs, config)
10
+ - [ ] 2.2 实现数据库初始化脚本
11
+ - [ ] 2.3 实现数据库查询封装(CRUD 操作)
12
+ - [ ] 2.4 实现数据库迁移机制(可选)
13
+
14
+ ## 3. API 网关核心
15
+ - [ ] 3.1 实现网关服务器(Express 或 Next.js API Routes)
16
+ - [ ] 3.2 实现请求路由逻辑(根据模型 ID 路由到对应供应商)
17
+ - [ ] 3.3 实现协议适配器接口
18
+ - [ ] 3.4 实现 OpenAI 协议适配器
19
+ - [ ] 3.5 实现 Anthropic 协议适配器
20
+ - [ ] 3.6 实现 Gemini 协议适配器
21
+ - [ ] 3.7 实现请求转发和响应处理
22
+ - [ ] 3.8 实现错误处理和重试机制
23
+
24
+ ## 4. 模型供应商管理
25
+ - [ ] 4.1 实现供应商 CRUD API
26
+ - [ ] 4.2 实现供应商配置验证
27
+ - [ ] 4.3 实现 API Key 加密存储
28
+ - [ ] 4.4 实现供应商前端管理界面(添加、编辑、删除)
29
+ - [ ] 4.5 实现供应商列表展示
30
+
31
+ ## 5. 模型管理
32
+ - [ ] 5.1 实现模型 CRUD API
33
+ - [ ] 5.2 实现手动添加模型功能
34
+ - [ ] 5.3 实现自动拉取模型列表功能(调用供应商 API)
35
+ - [ ] 5.4 实现模型启用/禁用功能
36
+ - [ ] 5.5 实现模型前端管理界面
37
+ - [ ] 5.6 实现一键拉取按钮和交互
38
+
39
+ ## 6. 请求日志系统
40
+ - [ ] 6.1 实现请求日志记录中间件
41
+ - [ ] 6.2 实现日志存储到数据库
42
+ - [ ] 6.3 实现日志查询 API(分页、筛选)
43
+ - [ ] 6.4 实现日志列表前端界面
44
+ - [ ] 6.5 实现日志详情查看界面(JSON 美化展示)
45
+ - [ ] 6.6 实现敏感信息脱敏(API Key 等)
46
+
47
+ ## 7. Web 管理界面
48
+ - [ ] 7.1 设计主页面布局(使用 ui-ux-pro-max 优化)
49
+ - [ ] 7.2 实现网关配置界面(端口、API_KEY 设置)
50
+ - [ ] 7.3 实现启动/停止按钮和状态显示
51
+ - [ ] 7.4 实现供应商管理页面
52
+ - [ ] 7.5 实现模型管理页面
53
+ - [ ] 7.6 实现日志查看页面
54
+ - [ ] 7.7 使用 Tailwind CSS 美化所有界面
55
+ - [ ] 7.8 实现响应式设计
56
+
57
+ ## 8. CLI 工具
58
+ - [ ] 8.1 实现 CLI 入口文件
59
+ - [ ] 8.2 使用 Commander.js 实现命令解析
60
+ - [ ] 8.3 实现 `start` 命令(启动网关和 Web 界面)
61
+ - [ ] 8.4 实现配置管理命令(可选)
62
+ - [ ] 8.5 配置 package.json 的 bin 字段
63
+ - [ ] 8.6 测试 CLI 工具安装和使用
64
+
65
+ ## 9. 配置管理
66
+ - [ ] 9.1 实现配置存储(数据库或配置文件)
67
+ - [ ] 9.2 实现端口配置和验证
68
+ - [ ] 9.3 实现 API_KEY 配置和验证
69
+ - [ ] 9.4 实现配置持久化
70
+ - [ ] 9.5 实现配置前端界面
71
+
72
+ ## 10. 测试和优化
73
+ - [ ] 10.1 编写单元测试(核心功能)
74
+ - [ ] 10.2 测试各协议适配器
75
+ - [ ] 10.3 测试请求日志记录
76
+ - [ ] 10.4 性能测试和优化
77
+ - [ ] 10.5 错误处理测试
78
+
79
+ ## 11. 文档和发布
80
+ - [ ] 11.1 编写 README.md(使用说明、安装指南)
81
+ - [ ] 11.2 编写 API 文档(可选)
82
+ - [ ] 11.3 准备 npm 发布(package.json 配置、版本号)
83
+ - [ ] 11.4 测试 npm 安装和运行
84
+ - [ ] 11.5 发布到 npmjs
@@ -0,0 +1,58 @@
1
+ # Project Context
2
+
3
+ ## Purpose
4
+ AI Agent Router 是一个用于管理和路由 AI 代理的项目。项目包含多个 AI 助手技能(如 UI/UX Pro Max),使用 OpenSpec 进行规范驱动的开发,确保变更的可追溯性和一致性。
5
+
6
+ 主要目标:
7
+ - 提供结构化的 AI 代理技能管理
8
+ - 使用 OpenSpec 规范管理项目变更
9
+ - 支持多种 AI 助手平台(Claude, Cursor)
10
+
11
+ ## Tech Stack
12
+ - **Python 3** - 用于技能脚本(BM25 搜索算法、CSV 数据处理)
13
+ - **Markdown** - 规范文档和技能描述
14
+ - **Git** - 版本控制
15
+ - **OpenSpec** - 规范驱动开发框架
16
+
17
+ ## Project Conventions
18
+
19
+ ### Code Style
20
+ - **Python**: 使用 PEP 8 风格,UTF-8 编码(`# -*- coding: utf-8 -*-`)
21
+ - **文件命名**: kebab-case(如 `ui-ux-pro-max`)
22
+ - **目录结构**: 使用 `.claude/` 和 `.cursor/` 分别管理不同平台的配置
23
+ - **注释**: 使用中文或英文,保持清晰简洁
24
+
25
+ ### Architecture Patterns
26
+ - **技能模块化**: 每个技能独立目录,包含数据、脚本和文档
27
+ - **数据驱动**: 使用 CSV 文件存储结构化数据(样式、颜色、字体等)
28
+ - **搜索优先**: 使用 BM25 算法进行语义搜索,而非硬编码规则
29
+ - **规范驱动**: 使用 OpenSpec 管理所有变更,确保可追溯性
30
+
31
+ ### Testing Strategy
32
+ - 当前项目主要关注功能正确性
33
+ - Python 脚本应处理文件不存在、空数据等边界情况
34
+ - 建议添加单元测试验证 BM25 搜索算法和 CSV 解析逻辑
35
+
36
+ ### Git Workflow
37
+ - **主分支**: `main` 分支用于稳定代码
38
+ - **提交信息**: 使用清晰的中文或英文描述变更
39
+ - **变更管理**: 使用 OpenSpec 变更提案流程,变更完成后归档到 `changes/archive/`
40
+
41
+ ## Domain Context
42
+ - **AI Agent Skills**: 技能是模块化的功能单元,可以被 AI 助手调用
43
+ - **UI/UX Pro Max**: 当前主要技能,提供 UI/UX 设计智能搜索功能
44
+ - 支持 50+ 样式、21 调色板、50 字体配对、20 图表类型
45
+ - 支持 8 种技术栈(React, Next.js, Vue, Svelte, SwiftUI, React Native, Flutter, Tailwind)
46
+ - **BM25 搜索**: 使用 Okapi BM25 算法进行文本相关性排序
47
+ - **OpenSpec**: 规范驱动开发框架,通过 `proposal.md`、`tasks.md`、`design.md` 和规范增量管理变更
48
+
49
+ ## Important Constraints
50
+ - **文件路径**: 技能脚本需要处理相对路径,确保在不同目录下都能正确找到数据文件
51
+ - **编码**: 所有文本文件使用 UTF-8 编码,支持中文内容
52
+ - **平台兼容**: 需要同时支持 Claude 和 Cursor 平台,保持配置同步
53
+ - **数据格式**: CSV 文件必须包含表头,使用逗号分隔
54
+
55
+ ## External Dependencies
56
+ - **Python 3**: 必需,用于运行技能脚本
57
+ - **标准库**: 使用 Python 标准库(csv, re, pathlib, math, collections),无需额外依赖
58
+ - **OpenSpec CLI**: 用于验证和管理规范变更(如果已安装)
package/package.json ADDED
@@ -0,0 +1,51 @@
1
+ {
2
+ "name": "ai-agent-router",
3
+ "version": "0.1.0",
4
+ "description": "A unified API gateway for managing multiple AI model providers (Anthropic, OpenAI, Gemini, etc.)",
5
+ "main": "dist/src/cli/index.js",
6
+ "bin": {
7
+ "aar": "./dist/src/cli/index.js"
8
+ },
9
+ "scripts": {
10
+ "dev": "next dev",
11
+ "build": "next build && tsc",
12
+ "postbuild": "node scripts/fix-esm-imports.js",
13
+ "start": "node dist/src/cli/index.js start",
14
+ "lint": "next lint",
15
+ "type-check": "tsc --noEmit"
16
+ },
17
+ "keywords": [
18
+ "api-gateway",
19
+ "ai",
20
+ "openai",
21
+ "anthropic",
22
+ "gemini",
23
+ "proxy"
24
+ ],
25
+ "author": "",
26
+ "license": "MIT",
27
+ "dependencies": {
28
+ "next": "^14.0.0",
29
+ "react": "^18.2.0",
30
+ "react-dom": "^18.2.0",
31
+ "better-sqlite3": "^9.2.0",
32
+ "commander": "^11.1.0",
33
+ "crypto-js": "^4.2.0"
34
+ },
35
+ "devDependencies": {
36
+ "@types/node": "^20.10.0",
37
+ "@types/react": "^18.2.0",
38
+ "@types/react-dom": "^18.2.0",
39
+ "@types/better-sqlite3": "^7.6.0",
40
+ "@types/crypto-js": "^4.2.0",
41
+ "typescript": "^5.3.0",
42
+ "tailwindcss": "^3.3.0",
43
+ "autoprefixer": "^10.4.0",
44
+ "postcss": "^8.4.0",
45
+ "eslint": "^8.55.0",
46
+ "eslint-config-next": "^14.0.0"
47
+ },
48
+ "engines": {
49
+ "node": ">=18.0.0"
50
+ }
51
+ }
@@ -0,0 +1,6 @@
1
+ module.exports = {
2
+ plugins: {
3
+ tailwindcss: {},
4
+ autoprefixer: {},
5
+ },
6
+ }
@@ -0,0 +1,62 @@
1
+ import { NextRequest, NextResponse } from 'next/server';
2
+ import { getDatabase } from '@/db/database';
3
+ import { getConfig, setConfig, getAllConfig } from '@/db/queries';
4
+
5
+ // Ensure Node.js runtime (required for SQLite)
6
+ export const runtime = 'nodejs';
7
+
8
+ export async function GET(request: NextRequest) {
9
+ try {
10
+ // Initialize database on request (safer than module load)
11
+ getDatabase();
12
+
13
+ const { searchParams } = new URL(request.url);
14
+ const key = searchParams.get('key');
15
+
16
+ if (key) {
17
+ const config = getConfig(key);
18
+ if (!config) {
19
+ return NextResponse.json(
20
+ { error: 'Config not found' },
21
+ { status: 404 }
22
+ );
23
+ }
24
+ return NextResponse.json(config);
25
+ }
26
+
27
+ const configs = getAllConfig();
28
+ return NextResponse.json(configs);
29
+ } catch (error: any) {
30
+ console.error('Config API error:', error);
31
+ return NextResponse.json(
32
+ { error: error.message || 'Internal server error', stack: process.env.NODE_ENV === 'development' ? error.stack : undefined },
33
+ { status: 500 }
34
+ );
35
+ }
36
+ }
37
+
38
+ export async function POST(request: NextRequest) {
39
+ try {
40
+ // Initialize database on request
41
+ getDatabase();
42
+
43
+ const body = await request.json();
44
+ const { key, value } = body;
45
+
46
+ if (!key || value === undefined) {
47
+ return NextResponse.json(
48
+ { error: 'Key and value are required' },
49
+ { status: 400 }
50
+ );
51
+ }
52
+
53
+ const config = setConfig(key, typeof value === 'string' ? value : JSON.stringify(value));
54
+ return NextResponse.json(config);
55
+ } catch (error: any) {
56
+ console.error('Config POST API error:', error);
57
+ return NextResponse.json(
58
+ { error: error.message || 'Internal server error', stack: process.env.NODE_ENV === 'development' ? error.stack : undefined },
59
+ { status: 500 }
60
+ );
61
+ }
62
+ }
@@ -0,0 +1,118 @@
1
+ import { NextRequest, NextResponse } from 'next/server';
2
+ import { getDatabase } from '@/db/database';
3
+ import { handleGatewayRequest } from '@/server/gateway';
4
+
5
+ // Ensure Node.js runtime (required for SQLite)
6
+ export const runtime = 'nodejs';
7
+
8
+ export async function POST(
9
+ request: NextRequest,
10
+ { params }: { params: { path: string[] } }
11
+ ) {
12
+ return handleRequest(request, params, 'POST');
13
+ }
14
+
15
+ export async function GET(
16
+ request: NextRequest,
17
+ { params }: { params: { path: string[] } }
18
+ ) {
19
+ return handleRequest(request, params, 'GET');
20
+ }
21
+
22
+ export async function PUT(
23
+ request: NextRequest,
24
+ { params }: { params: { path: string[] } }
25
+ ) {
26
+ return handleRequest(request, params, 'PUT');
27
+ }
28
+
29
+ export async function PATCH(
30
+ request: NextRequest,
31
+ { params }: { params: { path: string[] } }
32
+ ) {
33
+ return handleRequest(request, params, 'PATCH');
34
+ }
35
+
36
+ export async function DELETE(
37
+ request: NextRequest,
38
+ { params }: { params: { path: string[] } }
39
+ ) {
40
+ return handleRequest(request, params, 'DELETE');
41
+ }
42
+
43
+ async function handleRequest(
44
+ request: NextRequest,
45
+ params: { path: string[] },
46
+ method: string
47
+ ) {
48
+ try {
49
+ getDatabase();
50
+ // Extract model ID from path or query
51
+ // Common patterns: /v1/models/{model_id}/... or ?model=...
52
+ const path = params.path.join('/');
53
+ const searchParams = request.nextUrl.searchParams;
54
+
55
+ // Try to get model from query or path
56
+ let modelId = searchParams.get('model') || searchParams.get('model_id');
57
+ const providerName = searchParams.get('provider');
58
+
59
+ // Get request body first (needed for both model ID and provider)
60
+ let body: any = null;
61
+ try {
62
+ if (method !== 'GET' && method !== 'HEAD') {
63
+ const text = await request.text();
64
+ if (text) {
65
+ body = JSON.parse(text);
66
+ }
67
+ }
68
+ } catch {
69
+ // Body might not be JSON
70
+ }
71
+
72
+ // If not in query, try to extract from path (e.g., /v1/models/gpt-4/completions)
73
+ if (!modelId) {
74
+ const pathParts = path.split('/');
75
+ const modelsIndex = pathParts.indexOf('models');
76
+ if (modelsIndex >= 0 && pathParts[modelsIndex + 1]) {
77
+ modelId = pathParts[modelsIndex + 1];
78
+ }
79
+ }
80
+
81
+ // If still no model, try to get from body
82
+ if (!modelId && body) {
83
+ modelId = body.model || body.model_id;
84
+ }
85
+ const bodyProvider = body?.provider;
86
+
87
+ if (!modelId) {
88
+ return NextResponse.json(
89
+ { error: { message: 'Model ID not specified' } },
90
+ { status: 400 }
91
+ );
92
+ }
93
+
94
+ // Build gateway request
95
+ const gatewayRequest = {
96
+ method,
97
+ path: '/' + path,
98
+ headers: Object.fromEntries(request.headers.entries()),
99
+ query: Object.fromEntries(searchParams.entries()),
100
+ body,
101
+ };
102
+
103
+ // Handle the request (pass provider name if specified)
104
+ const finalProviderName = providerName || bodyProvider;
105
+ const response = await handleGatewayRequest(modelId, gatewayRequest, finalProviderName || undefined);
106
+
107
+ // Return response
108
+ return NextResponse.json(response.body, {
109
+ status: response.status,
110
+ headers: response.headers,
111
+ });
112
+ } catch (error: any) {
113
+ return NextResponse.json(
114
+ { error: { message: error.message || 'Internal server error' } },
115
+ { status: 500 }
116
+ );
117
+ }
118
+ }
@@ -0,0 +1,77 @@
1
+ import { NextRequest, NextResponse } from 'next/server';
2
+ import { getDatabase } from '@/db/database';
3
+ import { handleGatewayRequest } from '@/server/gateway';
4
+
5
+ // Ensure Node.js runtime (required for SQLite)
6
+ export const runtime = 'nodejs';
7
+
8
+ // Handle requests to /api/gateway (root gateway endpoint)
9
+ export async function POST(request: NextRequest) {
10
+ return handleGatewayRequestDirect(request, 'POST');
11
+ }
12
+
13
+ export async function GET(request: NextRequest) {
14
+ return handleGatewayRequestDirect(request, 'GET');
15
+ }
16
+
17
+ async function handleGatewayRequestDirect(
18
+ request: NextRequest,
19
+ method: string
20
+ ) {
21
+ try {
22
+ getDatabase();
23
+ const searchParams = request.nextUrl.searchParams;
24
+ let modelId = searchParams.get('model') || searchParams.get('model_id');
25
+ const providerName = searchParams.get('provider');
26
+
27
+ // Get request body
28
+ let body: any = null;
29
+ try {
30
+ if (method !== 'GET' && method !== 'HEAD') {
31
+ const text = await request.text();
32
+ if (text) {
33
+ body = JSON.parse(text);
34
+ }
35
+ }
36
+ } catch {
37
+ // Body might not be JSON
38
+ }
39
+
40
+ // Try to get from body
41
+ if (!modelId && body) {
42
+ modelId = body.model || body.model_id;
43
+ }
44
+ const bodyProvider = body?.provider;
45
+
46
+ if (!modelId) {
47
+ return NextResponse.json(
48
+ { error: { message: 'Model ID not specified' } },
49
+ { status: 400 }
50
+ );
51
+ }
52
+
53
+ // Build gateway request
54
+ const gatewayRequest = {
55
+ method,
56
+ path: '/',
57
+ headers: Object.fromEntries(request.headers.entries()),
58
+ query: Object.fromEntries(searchParams.entries()),
59
+ body,
60
+ };
61
+
62
+ // Handle the request (pass provider name if specified)
63
+ const finalProviderName = providerName || bodyProvider;
64
+ const response = await handleGatewayRequest(modelId, gatewayRequest, finalProviderName || undefined);
65
+
66
+ // Return response
67
+ return NextResponse.json(response.body, {
68
+ status: response.status,
69
+ headers: response.headers,
70
+ });
71
+ } catch (error: any) {
72
+ return NextResponse.json(
73
+ { error: { message: error.message || 'Internal server error' } },
74
+ { status: 500 }
75
+ );
76
+ }
77
+ }