aicodeswitch 2.0.10 → 2.1.1
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/CHANGELOG.md +4 -0
- package/CLAUDE.md +32 -2
- package/TECH.md +292 -170
- package/dist/server/main.js +38 -0
- package/dist/server/proxy-server.js +116 -14
- package/dist/server/rules-status-service.js +197 -0
- package/dist/server/tools-service.js +202 -0
- package/dist/server/transformers/claude-openai.js +554 -48
- package/dist/server/transformers/streaming.js +169 -2
- package/dist/server/websocket-service.js +148 -0
- package/dist/ui/assets/index-B5wnowv6.css +1 -0
- package/dist/ui/assets/index-DXvVJfvE.js +452 -0
- package/dist/ui/index.html +2 -2
- package/package.json +3 -2
- package/schemes/claude.schema.md +945 -0
- package/schemes/openai.schema.md +2162 -0
- package/dist/ui/assets/index-BYeFnkER.js +0 -431
- package/dist/ui/assets/index-DQ2LJr-O.css +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
|
4
4
|
|
|
5
|
+
### 2.1.1 (2026-02-03)
|
|
6
|
+
|
|
7
|
+
### 2.0.11 (2026-02-03)
|
|
8
|
+
|
|
5
9
|
### 2.0.10 (2026-02-03)
|
|
6
10
|
|
|
7
11
|
### 2.0.9 (2026-02-02)
|
package/CLAUDE.md
CHANGED
|
@@ -99,8 +99,27 @@ aicos version # Show current version information
|
|
|
99
99
|
#### 3. Transformers - `server/transformers/`
|
|
100
100
|
- **streaming.ts**: SSE parsing/serialization and event transformation
|
|
101
101
|
- **claude-openai.ts**: Claude ↔ OpenAI Chat format conversion
|
|
102
|
+
- Image content block conversion (Claude ↔ OpenAI formats)
|
|
103
|
+
- Tool choice mapping (auto/any/tool ↔ auto/none/required)
|
|
104
|
+
- Stop reason mapping (including max_thinking_length)
|
|
105
|
+
- System prompt handling (string and array formats)
|
|
106
|
+
- Thinking/Reasoning content conversion
|
|
102
107
|
- **chunk-collector.ts**: Collects streaming chunks for logging
|
|
103
108
|
|
|
109
|
+
**API 转换功能**:
|
|
110
|
+
转换器实现了以下 API 格式之间的双向转换:
|
|
111
|
+
- **Claude Messages API** ↔ **OpenAI Chat Completions API**
|
|
112
|
+
- **Claude Messages API** ↔ **OpenAI Responses API**
|
|
113
|
+
- **DeepSeek Chat** ↔ 其他格式(支持 developer 角色映射)
|
|
114
|
+
|
|
115
|
+
**支持的转换内容**:
|
|
116
|
+
- 文本内容 (text)
|
|
117
|
+
- 图像内容 (image ↔ image_url)
|
|
118
|
+
- 工具调用 (tool_use ↔ tool_calls)
|
|
119
|
+
- 工具结果 (tool_result)
|
|
120
|
+
- 思考内容 (thinking ↔ reasoning/thinking)
|
|
121
|
+
- 系统提示词 (system - 支持字符串和数组格式)
|
|
122
|
+
|
|
104
123
|
#### 4. Database - `server/database.ts`
|
|
105
124
|
- SQLite3 database wrapper
|
|
106
125
|
- Manages: Vendors, API Services, Routes, Rules, Logs
|
|
@@ -139,7 +158,7 @@ aicos version # Show current version information
|
|
|
139
158
|
- `image-understanding`: Requests with image content
|
|
140
159
|
- `thinking`: Requests with reasoning/thinking signals
|
|
141
160
|
- `long-context`: Requests with large context (≥12000 chars or ≥8000 max tokens)
|
|
142
|
-
- `background`: Background/priority requests
|
|
161
|
+
- `background`: Background/priority requests, including `/count_tokens` endpoint requests and token counting requests with `{"role": "user", "content": "count"}`
|
|
143
162
|
- `default`: All other requests
|
|
144
163
|
|
|
145
164
|
### Request Transformation
|
|
@@ -163,7 +182,18 @@ aicos version # Show current version information
|
|
|
163
182
|
### Logging
|
|
164
183
|
- Request logs: Detailed API call records with token usage
|
|
165
184
|
- Access logs: System access records
|
|
166
|
-
- Error logs: Error and exception records
|
|
185
|
+
- Error logs: Error and exception records with comprehensive context
|
|
186
|
+
- **Error Log Details**:
|
|
187
|
+
- Basic error information: timestamp, method, path, status code, error message, error stack
|
|
188
|
+
- Request context: targetType (client type), requestModel (requested model)
|
|
189
|
+
- Routing context: ruleId (used rule), targetServiceId/Name (API service), targetModel (actual model)
|
|
190
|
+
- Vendor context: vendorId/Name (service provider)
|
|
191
|
+
- Request details: request headers, request body, response headers, response body
|
|
192
|
+
- **Upstream Request Information**: URL, headers, body, proxy usage
|
|
193
|
+
- Response time metrics
|
|
194
|
+
- **Data Sanitization**:
|
|
195
|
+
- Sensitive authentication fields (api_key, authorization, password, secret, etc.) are automatically masked in the UI
|
|
196
|
+
- Technical fields like `max_tokens`, `input_tokens`, `output_tokens` are NOT masked - they are legitimate API parameters
|
|
167
197
|
- **Session Management**:
|
|
168
198
|
- Tracks user sessions based on session ID (Claude Code: `metadata.user_id`, Codex: `headers.session_id`)
|
|
169
199
|
- Auto-generates session title from first user message content:
|
package/TECH.md
CHANGED
|
@@ -10,184 +10,306 @@ AI semantic search powered by Cloudflare AI
|
|
|
10
10
|
Parameter Type Required Description
|
|
11
11
|
q string ✓ AI search query
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
---
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
'https://skillsmp.com/api/v1/skills/ai-search?q=How+to+create+a+web+scraper',
|
|
17
|
-
{
|
|
18
|
-
headers: {
|
|
19
|
-
'Authorization': 'Bearer sk_live_skillsmp_SNqsutoSiH51g-7-E0zVFVuugcnXfQbxCqfDI786TI0'
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
);
|
|
15
|
+
## Thinking/Reasoning 功能实现
|
|
23
16
|
|
|
24
|
-
|
|
25
|
-
console.log(data.data.skills);
|
|
17
|
+
### 概述
|
|
26
18
|
|
|
27
|
-
Responses
|
|
19
|
+
实现了 Claude 与 OpenAI 系列 API(Chat Completions、Responses、DeepSeek)之间的 thinking/reasoning 功能的双向转换。
|
|
20
|
+
|
|
21
|
+
### 设计思路
|
|
22
|
+
|
|
23
|
+
1. **统一抽象**:Claude 的 `thinking` 配置与 OpenAI 的 `reasoning` 配置虽有语义差异,但核心概念相似
|
|
24
|
+
2. **智能映射**:根据不同 API 规范进行自适应转换
|
|
25
|
+
3. **流式支持**:完整支持流式响应中的 thinking 内容转换
|
|
26
|
+
|
|
27
|
+
### 关键实现
|
|
28
|
+
|
|
29
|
+
#### 1. 请求配置转换
|
|
30
|
+
|
|
31
|
+
| 源格式 | 目标 API | 转换逻辑 |
|
|
32
|
+
|--------|----------|----------|
|
|
33
|
+
| `thinking: { type: "enabled" }` | OpenAI Chat | `thinking: { type: "enabled" }` |
|
|
34
|
+
| `thinking: { type: "enabled" }` | OpenAI Responses | `thinking: { type: "enabled" }, reasoning: { effort: "medium" }` |
|
|
35
|
+
| `thinking: { type: "disabled" }` | OpenAI Responses | `thinking: { type: "disabled" }, reasoning: { effort: "minimal" }` |
|
|
36
|
+
| `thinking: { type: "auto" }` | OpenAI Responses | `thinking: { type: "auto" }, reasoning: { effort: "low" }` |
|
|
37
|
+
| `reasoning_effort: "high"` | OpenAI Responses | `reasoning: { effort: "high" }` |
|
|
38
|
+
|
|
39
|
+
#### 2. Content Block 处理
|
|
40
|
+
|
|
41
|
+
- **Claude → OpenAI**:thinking content block 转换为特殊标记文本 `<thinking>...</thinking>`
|
|
42
|
+
- **OpenAI → Claude**:识别多种格式(`message.thinking`、`reasoning.summary`、`output[].content`)并转换为 thinking block
|
|
43
|
+
|
|
44
|
+
#### 3. 流式事件转换
|
|
45
|
+
|
|
46
|
+
**OpenAI → Claude**:
|
|
47
|
+
- `delta.thinking.content` → `thinking_delta` 事件
|
|
48
|
+
- `response.reasoning_text.delta` → `thinking_delta` 事件
|
|
49
|
+
- `response.reasoning_summary_text.delta` → `thinking_delta` 事件
|
|
50
|
+
- `response.output_text.delta` → `text_delta` 事件
|
|
51
|
+
|
|
52
|
+
**Claude → OpenAI**:
|
|
53
|
+
- `thinking_delta` → `delta.thinking.content`
|
|
54
|
+
|
|
55
|
+
### 文件位置
|
|
56
|
+
|
|
57
|
+
- `src/server/transformers/claude-openai.ts` - 请求/响应转换
|
|
58
|
+
- `src/server/transformers/streaming.ts` - 流式事件转换
|
|
59
|
+
|
|
60
|
+
### 注意事项
|
|
61
|
+
|
|
62
|
+
1. **类型安全**:使用 `(any)` 类型断言避免 TypeScript 类型错误
|
|
63
|
+
2. **向后兼容**:保持对旧格式响应的支持
|
|
64
|
+
3. **错误处理**:流式转换器中捕获异常避免中断整个流
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
## API 转换架构
|
|
69
|
+
|
|
70
|
+
### 概述
|
|
71
|
+
|
|
72
|
+
AI Code Switch 实现了多种 AI API 格式之间的双向转换,支持 Claude、OpenAI Chat Completions、OpenAI Responses、DeepSeek 等主流 API。
|
|
73
|
+
|
|
74
|
+
### 支持的 API 格式
|
|
75
|
+
|
|
76
|
+
| API 类型 | 端点 | 主要用途 | 状态 |
|
|
77
|
+
|---------|------|----------|------|
|
|
78
|
+
| **Claude Messages API** | `/v1/messages` | Anthropic 官方 API | ✅ 完整支持 |
|
|
79
|
+
| **OpenAI Chat Completions** | `/v1/chat/completions` | OpenAI 对话 API | ✅ 完整支持 |
|
|
80
|
+
| **OpenAI Responses API** | `/v1/responses` | OpenAI 新一代响应接口 | ✅ 完整支持 |
|
|
81
|
+
| **DeepSeek Chat** | `/v1/chat/completions` | DeepSeek 对话 API | ✅ 完整支持(developer 角色) |
|
|
82
|
+
|
|
83
|
+
### API 转换矩阵
|
|
84
|
+
|
|
85
|
+
| 源 API | 目标 API | 转换函数 | 支持状态 |
|
|
86
|
+
|--------|----------|----------|----------|
|
|
87
|
+
| Claude Messages | OpenAI Chat | [`transformClaudeRequestToOpenAIChat()`](src/server/transformers/claude-openai.ts#L278) | ✅ 完整 |
|
|
88
|
+
| Claude Messages | OpenAI Responses | `transformClaudeRequestToOpenAIChat()` + reasoning 映射 | ✅ 完整 |
|
|
89
|
+
| Claude Messages | DeepSeek Chat | `transformClaudeRequestToOpenAIChat()` + developer 角色映射 | ✅ 完整 |
|
|
90
|
+
| OpenAI Chat | Claude Messages | [`transformOpenAIChatResponseToClaude()`](src/server/transformers/claude-openai.ts#L363) | ✅ 完整 |
|
|
91
|
+
| OpenAI Responses | Claude Messages | 流式事件转换 + [`transformResponsesToChatCompletions()`](src/server/transformers/claude-openai.ts#L584) | ✅ 完整 |
|
|
92
|
+
| DeepSeek Chat | Claude Messages | `transformOpenAIChatResponseToClaude()` | ✅ 完整 |
|
|
93
|
+
| OpenAI Chat | OpenAI Responses | [`transformChatCompletionsToResponses()`](src/server/transformers/claude-openai.ts#L511) | ✅ 新增 |
|
|
94
|
+
| OpenAI Responses | OpenAI Chat | [`transformResponsesToChatCompletions()`](src/server/transformers/claude-openai.ts#L584) | ✅ 新增 |
|
|
95
|
+
|
|
96
|
+
### 内容块转换
|
|
97
|
+
|
|
98
|
+
#### 文本内容 (Text)
|
|
99
|
+
```typescript
|
|
100
|
+
// Claude
|
|
101
|
+
{ type: "text", text: "Hello" }
|
|
102
|
+
|
|
103
|
+
// OpenAI
|
|
104
|
+
{ type: "text", text: "Hello" } // 或直接 "Hello"
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
#### 图像内容 (Image)
|
|
108
|
+
```typescript
|
|
109
|
+
// Claude
|
|
28
110
|
{
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
"object": "vector_store.search_results.page",
|
|
32
|
-
"search_query": "How to create a web scraper",
|
|
33
|
-
"data": [
|
|
34
|
-
{
|
|
35
|
-
"file_id": "b941f4a570315d69f10272c602473c47f04bdd75fb714311ed36ea5b7029b1e5",
|
|
36
|
-
"filename": "skills/mattb543-asheville-event-feed-claude-event-scraper-skill-md.md",
|
|
37
|
-
"score": 0.58138084,
|
|
38
|
-
"skill": {
|
|
39
|
-
"id": "mattb543-asheville-event-feed-claude-event-scraper-skill-md",
|
|
40
|
-
"name": "event-scraper",
|
|
41
|
-
"author": "MattB543",
|
|
42
|
-
"description": "Create new event scraping scripts for websites. Use when adding a new event source to the Asheville Event Feed. ALWAYS start by detecting the CMS/platform and trying known API endpoints first. Browser scraping is NOT supported (Vercel limitation). Handles API-based, HTML/JSON-LD, and hybrid patterns with comprehensive testing workflows.",
|
|
43
|
-
"githubUrl": "https://github.com/MattB543/asheville-event-feed/tree/main/claude/event-scraper",
|
|
44
|
-
"skillUrl": "https://skillsmp.com/skills/mattb543-asheville-event-feed-claude-event-scraper-skill-md",
|
|
45
|
-
"stars": 5,
|
|
46
|
-
"updatedAt": 1768598524
|
|
47
|
-
}
|
|
48
|
-
},
|
|
49
|
-
{
|
|
50
|
-
"file_id": "34e6c58d525232653f2adfdf8bc5abf9b50eb9128b5abe68a630d656ca9801c9",
|
|
51
|
-
"filename": "skills/dvorkinguy-claude-skills-agents-skills-apify-scraper-builder-skill-md.md",
|
|
52
|
-
"score": 0.5716883
|
|
53
|
-
},
|
|
54
|
-
{
|
|
55
|
-
"file_id": "7dcf28e65a11cb6fb0e205bc9b3ad1ce9007724f809632ccd81f42c3167bd688",
|
|
56
|
-
"filename": "skills/honeyspoon-nix-config-config-opencode-skill-web-scraper-skill-md.md",
|
|
57
|
-
"score": 0.5999056,
|
|
58
|
-
"skill": {
|
|
59
|
-
"id": "honeyspoon-nix-config-config-opencode-skill-web-scraper-skill-md",
|
|
60
|
-
"name": "web-scraper",
|
|
61
|
-
"author": "honeyspoon",
|
|
62
|
-
"description": "This skill should be used when users need to scrape content from websites, extract text from web pages, crawl and follow links, or download documentation from online sources. It features concurrent URL processing, automatic deduplication, content filtering, domain restrictions, and proper directory hierarchy based on URL structure. Use for documentation gathering, content extraction, web archival, or research data collection.",
|
|
63
|
-
"githubUrl": "https://github.com/honeyspoon/nix_config/tree/main/config/opencode/skill/web-scraper",
|
|
64
|
-
"skillUrl": "https://skillsmp.com/skills/honeyspoon-nix-config-config-opencode-skill-web-scraper-skill-md",
|
|
65
|
-
"stars": 0,
|
|
66
|
-
"updatedAt": 1769614394
|
|
67
|
-
}
|
|
68
|
-
},
|
|
69
|
-
{
|
|
70
|
-
"file_id": "1f2f1810d2d63396a79130bbb59849ad776fdcc6242e57120528d626d7da4adc",
|
|
71
|
-
"filename": "skills/igosuki-claude-skills-web-scraper-skill-md.md",
|
|
72
|
-
"score": 0.5999056,
|
|
73
|
-
"skill": {
|
|
74
|
-
"id": "igosuki-claude-skills-web-scraper-skill-md",
|
|
75
|
-
"name": "web-scraper",
|
|
76
|
-
"author": "Igosuki",
|
|
77
|
-
"description": "This skill should be used when users need to scrape content from websites, extract text from web pages, crawl and follow links, or download documentation from online sources. It features concurrent URL processing, automatic deduplication, content filtering, domain restrictions, and proper directory hierarchy based on URL structure. Use for documentation gathering, content extraction, web archival, or research data collection.",
|
|
78
|
-
"githubUrl": "https://github.com/Igosuki/claude-skills/tree/main/web-scraper",
|
|
79
|
-
"skillUrl": "https://skillsmp.com/skills/igosuki-claude-skills-web-scraper-skill-md",
|
|
80
|
-
"stars": 1,
|
|
81
|
-
"updatedAt": 1761052646
|
|
82
|
-
}
|
|
83
|
-
},
|
|
84
|
-
{
|
|
85
|
-
"file_id": "8bc3072e2ca3c703aae8ec4cb48be2a3c0826d96b4f22585788d34f1ddd9cbda",
|
|
86
|
-
"filename": "skills/breverdbidder-life-os-skills-website-to-vite-scraper-skill-md.md",
|
|
87
|
-
"score": 0.5806182,
|
|
88
|
-
"skill": {
|
|
89
|
-
"id": "breverdbidder-life-os-skills-website-to-vite-scraper-skill-md",
|
|
90
|
-
"name": "website-to-vite-scraper",
|
|
91
|
-
"author": "breverdbidder",
|
|
92
|
-
"description": "Multi-provider website scraper that converts any website (including CSR/SPA) to deployable static sites. Uses Playwright, Apify RAG Browser, Crawl4AI, and Firecrawl for comprehensive scraping. Triggers on requests to clone, reverse-engineer, or convert websites.",
|
|
93
|
-
"githubUrl": "https://github.com/breverdbidder/life-os/tree/main/skills/website-to-vite-scraper",
|
|
94
|
-
"skillUrl": "https://skillsmp.com/skills/breverdbidder-life-os-skills-website-to-vite-scraper-skill-md",
|
|
95
|
-
"stars": 2,
|
|
96
|
-
"updatedAt": 1769767370
|
|
97
|
-
}
|
|
98
|
-
},
|
|
99
|
-
{
|
|
100
|
-
"file_id": "8ed6e980ab048ac58d8c7d3bd3238fdc4cda3d25f17bc97b4dc4cfb2cf34cd35",
|
|
101
|
-
"filename": "skills/leobrival-serum-plugins-official-plugins-crawler-skills-website-crawler-skill-md.md",
|
|
102
|
-
"score": 0.5535727,
|
|
103
|
-
"skill": {
|
|
104
|
-
"id": "leobrival-serum-plugins-official-plugins-crawler-skills-website-crawler-skill-md",
|
|
105
|
-
"name": "website-crawler",
|
|
106
|
-
"author": "leobrival",
|
|
107
|
-
"description": "High-performance web crawler for discovering and mapping website structure. Use when users ask to crawl a website, map site structure, discover pages, find all URLs on a site, analyze link relationships, or generate site reports. Supports sitemap discovery, checkpoint/resume, rate limiting, and HTML report generation.",
|
|
108
|
-
"githubUrl": "https://github.com/leobrival/serum-plugins-official/tree/main/plugins/crawler/skills/website-crawler",
|
|
109
|
-
"skillUrl": "https://skillsmp.com/skills/leobrival-serum-plugins-official-plugins-crawler-skills-website-crawler-skill-md",
|
|
110
|
-
"stars": 1,
|
|
111
|
-
"updatedAt": 1769437425
|
|
112
|
-
}
|
|
113
|
-
},
|
|
114
|
-
{
|
|
115
|
-
"file_id": "efa5af3ef929da6b1db4f194da6d639600db620612b508e425099947215f480a",
|
|
116
|
-
"filename": "skills/hokupod-sitepanda-assets-skill-md.md",
|
|
117
|
-
"score": 0.57184756,
|
|
118
|
-
"skill": {
|
|
119
|
-
"id": "hokupod-sitepanda-assets-skill-md",
|
|
120
|
-
"name": "sitepanda",
|
|
121
|
-
"author": "hokupod",
|
|
122
|
-
"description": "Scrape websites with a headless browser and extract main readable content as Markdown. Use this skill when the user asks to retrieve, analyze, or summarize content from a URL or website.",
|
|
123
|
-
"githubUrl": "https://github.com/hokupod/sitepanda/tree/main/assets",
|
|
124
|
-
"skillUrl": "https://skillsmp.com/skills/hokupod-sitepanda-assets-skill-md",
|
|
125
|
-
"stars": 10,
|
|
126
|
-
"updatedAt": 1768397847
|
|
127
|
-
}
|
|
128
|
-
},
|
|
129
|
-
{
|
|
130
|
-
"file_id": "e74a45c1d4c2646144b1019bb8a4e52aa4352c52a1b35566234a68bf057c666a",
|
|
131
|
-
"filename": "skills/vanman2024-ai-dev-marketplace-plugins-rag-pipeline-skills-web-scraping-tools-skill-md.md",
|
|
132
|
-
"score": 0.55511314
|
|
133
|
-
},
|
|
134
|
-
{
|
|
135
|
-
"file_id": "9b11ec7c719303b2999eaf4fa535a26ffcf718ea773f579e5e6b5b8d046cce12",
|
|
136
|
-
"filename": "skills/nathanvale-side-quest-marketplace-plugins-scraper-toolkit-skills-playwright-scraper-skill-md.md",
|
|
137
|
-
"score": 0.54990166,
|
|
138
|
-
"skill": {
|
|
139
|
-
"id": "nathanvale-side-quest-marketplace-plugins-scraper-toolkit-skills-playwright-scraper-skill-md",
|
|
140
|
-
"name": "playwright-scraper",
|
|
141
|
-
"author": "nathanvale",
|
|
142
|
-
"description": "Production-proven Playwright web scraping patterns with selector-first approach and robust error handling.\nUse when users need to build web scrapers, extract data from websites, automate browser interactions,\nor ask about Playwright selectors, text extraction (innerText vs textContent), regex patterns for HTML,\nfallback hierarchies, or scraping best practices.",
|
|
143
|
-
"githubUrl": "https://github.com/nathanvale/side-quest-marketplace/tree/main/plugins/scraper-toolkit/skills/playwright-scraper",
|
|
144
|
-
"skillUrl": "https://skillsmp.com/skills/nathanvale-side-quest-marketplace-plugins-scraper-toolkit-skills-playwright-scraper-skill-md",
|
|
145
|
-
"stars": 2,
|
|
146
|
-
"updatedAt": 1769733906
|
|
147
|
-
}
|
|
148
|
-
},
|
|
149
|
-
{
|
|
150
|
-
"file_id": "42348180a6ffcff196f8aa2b23797a0868a891174655e0c4df3245b4bfc530a0",
|
|
151
|
-
"filename": "skills/salberg87-authenticated-scrape-skill-md.md",
|
|
152
|
-
"score": 0.5437498,
|
|
153
|
-
"skill": {
|
|
154
|
-
"id": "salberg87-authenticated-scrape-skill-md",
|
|
155
|
-
"name": "authenticated-scrape",
|
|
156
|
-
"author": "Salberg87",
|
|
157
|
-
"description": "Scrape data from authenticated websites by capturing network requests with auth headers automatically. Use when the user wants to extract data from logged-in pages, private dashboards, or authenticated APIs.",
|
|
158
|
-
"githubUrl": "https://github.com/Salberg87/authenticated-scrape",
|
|
159
|
-
"skillUrl": "https://skillsmp.com/skills/salberg87-authenticated-scrape-skill-md",
|
|
160
|
-
"stars": 0,
|
|
161
|
-
"updatedAt": 1767684913
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
],
|
|
165
|
-
"has_more": false,
|
|
166
|
-
"next_page": null
|
|
167
|
-
},
|
|
168
|
-
"meta": {
|
|
169
|
-
"requestId": "f841d8bc-3d77-4a00-9899-4df8b4e52c86",
|
|
170
|
-
"responseTimeMs": 3327
|
|
171
|
-
}
|
|
111
|
+
type: "image",
|
|
112
|
+
source: { type: "base64", media_type: "image/jpeg", data: "..." }
|
|
172
113
|
}
|
|
173
114
|
|
|
174
|
-
|
|
175
|
-
|
|
115
|
+
// OpenAI
|
|
116
|
+
{
|
|
117
|
+
type: "image_url",
|
|
118
|
+
image_url: { url: "data:image/jpeg;base64,...", detail: "auto" }
|
|
119
|
+
}
|
|
120
|
+
```
|
|
121
|
+
**转换函数**: [`convertClaudeImageToOpenAI()`](src/server/transformers/claude-openai.ts#L35), [`convertOpenAIImageToClaude()`](src/server/transformers/claude-openai.ts#L79)
|
|
122
|
+
|
|
123
|
+
#### 思考内容 (Thinking)
|
|
124
|
+
```typescript
|
|
125
|
+
// Claude
|
|
126
|
+
{ type: "thinking", thinking: "Let me analyze..." }
|
|
176
127
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
INVALID_API_KEY 401 Invalid API key
|
|
180
|
-
MISSING_QUERY 400 Missing required query parameter
|
|
181
|
-
INTERNAL_ERROR 500 Internal server error
|
|
182
|
-
Error Response Example:
|
|
128
|
+
// OpenAI Chat
|
|
129
|
+
{ thinking: { content: "Let me analyze..." } } // 或 delta.thinking.content
|
|
183
130
|
|
|
184
|
-
|
|
131
|
+
// OpenAI Responses
|
|
132
|
+
{ type: "thinking", text: "Let me analyze..." } // 在 output[].content 中
|
|
133
|
+
```
|
|
185
134
|
|
|
186
|
-
|
|
135
|
+
#### 工具调用 (Tool Calls)
|
|
136
|
+
```typescript
|
|
137
|
+
// Claude
|
|
138
|
+
{ type: "tool_use", id: "call_123", name: "get_weather", input: {...} }
|
|
139
|
+
|
|
140
|
+
// OpenAI
|
|
187
141
|
{
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
}
|
|
142
|
+
tool_calls: [{
|
|
143
|
+
id: "call_123",
|
|
144
|
+
type: "function",
|
|
145
|
+
function: { name: "get_weather", arguments: "{...}" }
|
|
146
|
+
}]
|
|
193
147
|
}
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
#### 工具结果 (Tool Results)
|
|
151
|
+
```typescript
|
|
152
|
+
// Claude
|
|
153
|
+
{ type: "tool_result", tool_use_id: "call_123", content: "..." }
|
|
154
|
+
|
|
155
|
+
// OpenAI
|
|
156
|
+
{ role: "tool", tool_call_id: "call_123", content: "..." }
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
### 参数转换映射
|
|
160
|
+
|
|
161
|
+
#### 温度参数 (Temperature)
|
|
162
|
+
- 所有 API: `0.0 - 2.0` (OpenAI) / `0.0 - 1.0` (Claude)
|
|
163
|
+
- 直接传递,需注意范围差异
|
|
164
|
+
|
|
165
|
+
#### 停止序列 (Stop Sequences)
|
|
166
|
+
```typescript
|
|
167
|
+
// Claude
|
|
168
|
+
{ stop_sequences: ["END", "STOP"] }
|
|
169
|
+
|
|
170
|
+
// OpenAI
|
|
171
|
+
{ stop: ["END", "STOP"] } // 或 "END"
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
#### Token 限制
|
|
175
|
+
```typescript
|
|
176
|
+
// Claude
|
|
177
|
+
{ max_tokens: 4096 }
|
|
178
|
+
|
|
179
|
+
// OpenAI Chat
|
|
180
|
+
{ max_tokens: 4096 }
|
|
181
|
+
|
|
182
|
+
// OpenAI Responses
|
|
183
|
+
{ max_output_tokens: 4096 }
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
#### 工具选择 (Tool Choice)
|
|
187
|
+
```typescript
|
|
188
|
+
// Claude
|
|
189
|
+
{ tool_choice: "any" } // 或 { type: "tool", name: "func_name" }
|
|
190
|
+
|
|
191
|
+
// OpenAI
|
|
192
|
+
{ tool_choice: "required" } // 或 { type: "function", function: { name: "func_name" } }
|
|
193
|
+
```
|
|
194
|
+
**转换函数**: [`mapClaudeToolChoiceToOpenAI()`](src/server/transformers/claude-openai.ts#L136)
|
|
195
|
+
|
|
196
|
+
### Stop Reason 映射
|
|
197
|
+
|
|
198
|
+
#### OpenAI → Claude
|
|
199
|
+
| OpenAI finish_reason | Claude stop_reason |
|
|
200
|
+
|---------------------|-------------------|
|
|
201
|
+
| `stop` | `end_turn` |
|
|
202
|
+
| `length` | `max_tokens` |
|
|
203
|
+
| `tool_calls` | `tool_use` |
|
|
204
|
+
| `content_filter` | `content_filter` |
|
|
205
|
+
|
|
206
|
+
**转换函数**: [`mapStopReason()`](src/server/transformers/claude-openai.ts#L187)
|
|
207
|
+
|
|
208
|
+
#### Claude → OpenAI
|
|
209
|
+
| Claude stop_reason | OpenAI finish_reason |
|
|
210
|
+
|-------------------|---------------------|
|
|
211
|
+
| `end_turn` | `stop` |
|
|
212
|
+
| `max_tokens` | `length` |
|
|
213
|
+
| `max_thinking_length` | `length` |
|
|
214
|
+
| `tool_use` | `tool_calls` |
|
|
215
|
+
| `stop_sequence` | `stop` |
|
|
216
|
+
| `content_filter` | `content_filter` |
|
|
217
|
+
|
|
218
|
+
**转换函数**: [`mapClaudeStopReasonToOpenAI()`](src/server/transformers/claude-openai.ts#L207)
|
|
219
|
+
|
|
220
|
+
### 流式事件转换
|
|
221
|
+
|
|
222
|
+
#### OpenAI Chat → Claude
|
|
223
|
+
| OpenAI Chat Event | Claude Event | 转换位置 |
|
|
224
|
+
|------------------|--------------|----------|
|
|
225
|
+
| `data.choices[0].delta.content` | `content_block_delta` (text_delta) | [`OpenAIToClaudeEventTransform`](src/server/transformers/streaming.ts#L175) |
|
|
226
|
+
| `data.choices[0].delta.thinking.content` | `content_block_delta` (thinking_delta) | [`OpenAIToClaudeEventTransform`](src/server/transformers/streaming.ts#L334) |
|
|
227
|
+
| `data.choices[0].delta.tool_calls` | `content_block_delta` (input_json_delta) | [`OpenAIToClaudeEventTransform`](src/server/transformers/streaming.ts#L354) |
|
|
228
|
+
| `data.choices[0].finish_reason` | `message_delta` (stop_reason) | [`OpenAIToClaudeEventTransform`](src/server/transformers/streaming.ts#L504) |
|
|
229
|
+
|
|
230
|
+
#### OpenAI Responses → Claude
|
|
231
|
+
| Responses Event | Claude Event | 转换位置 |
|
|
232
|
+
|----------------|--------------|----------|
|
|
233
|
+
| `response.reasoning_text.delta` | `content_block_delta` (thinking_delta) | [`handleResponsesAPIEvent()`](src/server/transformers/streaming.ts#L408) |
|
|
234
|
+
| `response.reasoning_summary_text.delta` | `content_block_delta` (thinking_delta) | [`handleResponsesAPIEvent()`](src/server/transformers/streaming.ts#L429) |
|
|
235
|
+
| `response.output_text.delta` | `content_block_delta` (text_delta) | [`handleResponsesAPIEvent()`](src/server/transformers/streaming.ts#L450) |
|
|
236
|
+
| `response.refusal.delta` | `content_block_delta` (text_delta) | [`handleResponsesAPIEvent()`](src/server/transformers/streaming.ts#L471) |
|
|
237
|
+
| `response.completed/failed/incomplete` | `message_stop` | [`handleResponsesAPIEvent()`](src/server/transformers/streaming.ts#L500) |
|
|
238
|
+
|
|
239
|
+
#### Claude → OpenAI Chat
|
|
240
|
+
| Claude Event | OpenAI Chat Event | 转换位置 |
|
|
241
|
+
|--------------|------------------|----------|
|
|
242
|
+
| `content_block_delta` (text_delta) | `data.choices[0].delta.content` | [`ClaudeToOpenAIChatEventTransform`](src/server/transformers/streaming.ts#L622) |
|
|
243
|
+
| `content_block_delta` (thinking_delta) | `data.choices[0].delta.thinking.content` | [`ClaudeToOpenAIChatEventTransform`](src/server/transformers/streaming.ts#L628) |
|
|
244
|
+
| `content_block_delta` (input_json_delta) | `data.choices[0].delta.tool_calls` | [`ClaudeToOpenAIChatEventTransform`](src/server/transformers/streaming.ts#L594) |
|
|
245
|
+
| `message_delta` (stop_reason) | `data.choices[0].finish_reason` | [`ClaudeToOpenAIChatEventTransform`](src/server/transformers/streaming.ts#L632) |
|
|
246
|
+
|
|
247
|
+
### Token Usage 转换
|
|
248
|
+
|
|
249
|
+
```typescript
|
|
250
|
+
// OpenAI 格式
|
|
251
|
+
{
|
|
252
|
+
prompt_tokens: 100,
|
|
253
|
+
completion_tokens: 50,
|
|
254
|
+
total_tokens: 150,
|
|
255
|
+
prompt_tokens_details: { cached_tokens: 20 }
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// Claude 格式
|
|
259
|
+
{
|
|
260
|
+
input_tokens: 80, // prompt - cached
|
|
261
|
+
output_tokens: 50,
|
|
262
|
+
cache_read_input_tokens: 20 // cached
|
|
263
|
+
}
|
|
264
|
+
```
|
|
265
|
+
**转换函数**: [`convertOpenAIUsageToClaude()`](src/server/transformers/claude-openai.ts#L173)
|
|
266
|
+
|
|
267
|
+
### 特殊处理
|
|
268
|
+
|
|
269
|
+
#### DeepSeek Developer 角色
|
|
270
|
+
某些 OpenAI 兼容 API(如 DeepSeek)不支持 `system` 角色,需要使用 `developer` 角色。
|
|
271
|
+
|
|
272
|
+
**检测函数**: [`shouldUseDeveloperRole()`](src/server/transformers/claude-openai.ts#L229)
|
|
273
|
+
|
|
274
|
+
#### System 提示词数组
|
|
275
|
+
Claude 支持数组格式的 system 提示词(包含缓存控制),转换时会提取文本内容。
|
|
276
|
+
|
|
277
|
+
**处理位置**: [`transformClaudeRequestToOpenAIChat()`](src/server/transformers/claude-openai.ts#L169)
|
|
278
|
+
|
|
279
|
+
### 文件结构
|
|
280
|
+
|
|
281
|
+
```
|
|
282
|
+
src/server/transformers/
|
|
283
|
+
├── claude-openai.ts # 请求/响应转换
|
|
284
|
+
│ ├── transformClaudeRequestToOpenAIChat()
|
|
285
|
+
│ ├── transformOpenAIChatResponseToClaude()
|
|
286
|
+
│ ├── transformClaudeResponseToOpenAIChat()
|
|
287
|
+
│ ├── transformChatCompletionsToResponses()
|
|
288
|
+
│ ├── transformResponsesToChatCompletions()
|
|
289
|
+
│ ├── convertClaudeImageToOpenAI()
|
|
290
|
+
│ ├── convertOpenAIImageToClaude()
|
|
291
|
+
│ ├── mapClaudeToolChoiceToOpenAI()
|
|
292
|
+
│ ├── mapStopReason()
|
|
293
|
+
│ └── mapClaudeStopReasonToOpenAI()
|
|
294
|
+
└── streaming.ts # 流式事件转换
|
|
295
|
+
├── SSEParserTransform # SSE 解析
|
|
296
|
+
├── SSESerializerTransform # SSE 序列化
|
|
297
|
+
├── OpenAIToClaudeEventTransform # OpenAI → Claude 流式
|
|
298
|
+
├── ClaudeToOpenAIChatEventTransform # Claude → OpenAI 流式
|
|
299
|
+
└── handleResponsesAPIEvent() # Responses API 事件处理
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
### 开发注意事项
|
|
303
|
+
|
|
304
|
+
1. **类型安全**:转换函数使用 `any` 类型处理动态 API 格式
|
|
305
|
+
2. **向后兼容**:保持对旧版本 API 响应的支持
|
|
306
|
+
3. **错误恢复**:流式转换器捕获异常避免中断整个流
|
|
307
|
+
4. **性能优化**:使用数组格式构建消息(当包含图像时)
|
|
308
|
+
5. **完整测试**:覆盖所有转换路径和边界情况
|
|
309
|
+
|
|
310
|
+
### 相关文档
|
|
311
|
+
|
|
312
|
+
- [Claude API Schema](schemes/claude.schema.md) - Claude 官方 API 规范
|
|
313
|
+
- [OpenAI API Schema](schemes/openai.schema.md) - OpenAI 官方 API 规范
|
|
314
|
+
- [CLAUDE.md](CLAUDE.md) - 项目架构文档
|
|
315
|
+
|
package/dist/server/main.js
CHANGED
|
@@ -25,6 +25,9 @@ const os_1 = __importDefault(require("os"));
|
|
|
25
25
|
const auth_1 = require("./auth");
|
|
26
26
|
const version_check_1 = require("./version-check");
|
|
27
27
|
const utils_1 = require("./utils");
|
|
28
|
+
const tools_service_1 = require("./tools-service");
|
|
29
|
+
const websocket_service_1 = require("./websocket-service");
|
|
30
|
+
const rules_status_service_1 = require("./rules-status-service");
|
|
28
31
|
const config_metadata_1 = require("./config-metadata");
|
|
29
32
|
const config_1 = require("./config");
|
|
30
33
|
const appDir = path_1.default.join(os_1.default.homedir(), '.aicodeswitch');
|
|
@@ -1356,6 +1359,19 @@ ${instruction}
|
|
|
1356
1359
|
res.json({ success: false });
|
|
1357
1360
|
}
|
|
1358
1361
|
})));
|
|
1362
|
+
// 工具安装检测相关路由
|
|
1363
|
+
app.get('/api/tools/status', asyncHandler((_req, res) => __awaiter(void 0, void 0, void 0, function* () {
|
|
1364
|
+
console.log('[API] GET /api/tools/status - 获取工具安装状态');
|
|
1365
|
+
try {
|
|
1366
|
+
const status = yield (0, tools_service_1.getToolsInstallationStatus)();
|
|
1367
|
+
console.log('[API] 工具安装状态:', status);
|
|
1368
|
+
res.json(status);
|
|
1369
|
+
}
|
|
1370
|
+
catch (error) {
|
|
1371
|
+
console.error('[API] 获取工具状态失败:', error);
|
|
1372
|
+
res.status(500).json({ error: '获取工具状态失败' });
|
|
1373
|
+
}
|
|
1374
|
+
})));
|
|
1359
1375
|
};
|
|
1360
1376
|
const start = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
1361
1377
|
fs_1.default.mkdirSync(dataDir, { recursive: true });
|
|
@@ -1377,6 +1393,28 @@ const start = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
|
1377
1393
|
const server = app.listen(port, host, () => {
|
|
1378
1394
|
console.log(`Admin server running on http://${host}:${port}`);
|
|
1379
1395
|
});
|
|
1396
|
+
// 创建 WebSocket 服务器用于工具安装
|
|
1397
|
+
const toolInstallWss = (0, websocket_service_1.createToolInstallationWSServer)();
|
|
1398
|
+
// 创建 WebSocket 服务器用于规则状态
|
|
1399
|
+
const rulesStatusWss = (0, rules_status_service_1.createRulesStatusWSServer)();
|
|
1400
|
+
// 将 WebSocket 服务器附加到 HTTP 服务器
|
|
1401
|
+
server.on('upgrade', (request, socket, head) => {
|
|
1402
|
+
if (request.url === '/api/tools/install') {
|
|
1403
|
+
toolInstallWss.handleUpgrade(request, socket, head, (ws) => {
|
|
1404
|
+
toolInstallWss.emit('connection', ws, request);
|
|
1405
|
+
});
|
|
1406
|
+
}
|
|
1407
|
+
else if (request.url === '/api/rules/status') {
|
|
1408
|
+
rulesStatusWss.handleUpgrade(request, socket, head, (ws) => {
|
|
1409
|
+
rulesStatusWss.emit('connection', ws, request);
|
|
1410
|
+
});
|
|
1411
|
+
}
|
|
1412
|
+
else {
|
|
1413
|
+
socket.destroy();
|
|
1414
|
+
}
|
|
1415
|
+
});
|
|
1416
|
+
console.log(`WebSocket server for tool installation attached to ws://${host}:${port}/api/tools/install`);
|
|
1417
|
+
console.log(`WebSocket server for rules status attached to ws://${host}:${port}/api/rules/status`);
|
|
1380
1418
|
const shutdown = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
1381
1419
|
console.log('Shutting down server...');
|
|
1382
1420
|
dbManager.close();
|