blade-auth-service 1.0.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/README.md +81 -0
- package/dist/chatService.d.ts +47 -0
- package/dist/chatService.d.ts.map +1 -0
- package/dist/chatService.js +326 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +6 -0
- package/dist/keyService.d.ts +16 -0
- package/dist/keyService.d.ts.map +1 -0
- package/dist/keyService.js +47 -0
- package/dist/types.d.ts +76 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +4 -0
- package/package.json +35 -0
package/README.md
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# @blade/auth-service
|
|
2
|
+
|
|
3
|
+
Blade 私有认证服务 - 提供完整的 Claude ChatService 实现
|
|
4
|
+
|
|
5
|
+
## 功能
|
|
6
|
+
|
|
7
|
+
- ✅ 完整的 Claude API ChatService 实现
|
|
8
|
+
- ✅ 自动从 Cloudflare Worker 获取 API Key
|
|
9
|
+
- ✅ 支持流式和非流式响应
|
|
10
|
+
- ✅ 支持工具调用(Tool Use)
|
|
11
|
+
- ✅ 支持多模态(文本 + 图片)
|
|
12
|
+
- ✅ API Key 缓存机制
|
|
13
|
+
|
|
14
|
+
## 安装
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
# 从 GitHub Packages 安装
|
|
18
|
+
npm install @blade/auth-service
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## 使用
|
|
22
|
+
|
|
23
|
+
```typescript
|
|
24
|
+
import { BladeChatService } from '@blade/auth-service';
|
|
25
|
+
|
|
26
|
+
// 创建服务实例
|
|
27
|
+
const chatService = new BladeChatService({
|
|
28
|
+
model: 'claude-sonnet-4-20250514',
|
|
29
|
+
temperature: 0.7,
|
|
30
|
+
maxOutputTokens: 4096,
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
// 非流式调用
|
|
34
|
+
const response = await chatService.chat([
|
|
35
|
+
{ role: 'user', content: 'Hello, Claude!' }
|
|
36
|
+
]);
|
|
37
|
+
|
|
38
|
+
console.log(response.content);
|
|
39
|
+
|
|
40
|
+
// 流式调用
|
|
41
|
+
for await (const chunk of chatService.streamChat([
|
|
42
|
+
{ role: 'user', content: 'Tell me a story' }
|
|
43
|
+
])) {
|
|
44
|
+
if (chunk.content) {
|
|
45
|
+
process.stdout.write(chunk.content);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## API
|
|
51
|
+
|
|
52
|
+
### BladeChatService
|
|
53
|
+
|
|
54
|
+
#### 构造函数
|
|
55
|
+
|
|
56
|
+
```typescript
|
|
57
|
+
new BladeChatService(config: ChatConfig)
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
#### 方法
|
|
61
|
+
|
|
62
|
+
- `chat(messages, tools?, signal?)` - 非流式聊天
|
|
63
|
+
- `streamChat(messages, tools?, signal?)` - 流式聊天
|
|
64
|
+
- `getConfig()` - 获取配置
|
|
65
|
+
- `updateConfig(newConfig)` - 更新配置
|
|
66
|
+
|
|
67
|
+
## 架构
|
|
68
|
+
|
|
69
|
+
```
|
|
70
|
+
BladeChatService → Cloudflare Worker → 获取 API Key → Claude API
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## 安全性
|
|
74
|
+
|
|
75
|
+
- API Key 存储在 Cloudflare Worker 中
|
|
76
|
+
- 客户端永远不接触真实的 API Key
|
|
77
|
+
- 支持 IP 限流和使用量监控
|
|
78
|
+
|
|
79
|
+
## License
|
|
80
|
+
|
|
81
|
+
PRIVATE - 仅供 Blade 项目内部使用
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Blade 内置 Claude ChatService
|
|
3
|
+
* 完整的 ChatService 实现,API Key 从 Cloudflare Worker 获取
|
|
4
|
+
*/
|
|
5
|
+
import type { ChatConfig, ChatResponse, Message, StreamChunk, Tool as BladeToolType } from './types.js';
|
|
6
|
+
/**
|
|
7
|
+
* Blade 内置 Claude ChatService
|
|
8
|
+
*/
|
|
9
|
+
export declare class BladeChatService {
|
|
10
|
+
private client;
|
|
11
|
+
private config;
|
|
12
|
+
private initPromise;
|
|
13
|
+
constructor(config: ChatConfig);
|
|
14
|
+
/**
|
|
15
|
+
* 初始化客户端(懒加载)
|
|
16
|
+
*/
|
|
17
|
+
private ensureClient;
|
|
18
|
+
/**
|
|
19
|
+
* 转换消息格式
|
|
20
|
+
*/
|
|
21
|
+
private convertMessages;
|
|
22
|
+
/**
|
|
23
|
+
* 转换工具格式
|
|
24
|
+
*/
|
|
25
|
+
private convertTools;
|
|
26
|
+
/**
|
|
27
|
+
* 解析响应
|
|
28
|
+
*/
|
|
29
|
+
private parseResponse;
|
|
30
|
+
/**
|
|
31
|
+
* 发送聊天请求(非流式)
|
|
32
|
+
*/
|
|
33
|
+
chat(messages: Message[], tools?: BladeToolType[], signal?: AbortSignal): Promise<ChatResponse>;
|
|
34
|
+
/**
|
|
35
|
+
* 发送聊天请求(流式)
|
|
36
|
+
*/
|
|
37
|
+
streamChat(messages: Message[], tools?: BladeToolType[], signal?: AbortSignal): AsyncGenerator<StreamChunk, void, unknown>;
|
|
38
|
+
/**
|
|
39
|
+
* 获取配置
|
|
40
|
+
*/
|
|
41
|
+
getConfig(): ChatConfig;
|
|
42
|
+
/**
|
|
43
|
+
* 更新配置
|
|
44
|
+
*/
|
|
45
|
+
updateConfig(newConfig: Partial<ChatConfig>): void;
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=chatService.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chatService.d.ts","sourceRoot":"","sources":["../src/chatService.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAYH,OAAO,KAAK,EACV,UAAU,EACV,YAAY,EAEZ,OAAO,EACP,WAAW,EACX,IAAI,IAAI,aAAa,EAEtB,MAAM,YAAY,CAAC;AAsCpB;;GAEG;AACH,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,MAAM,CAA0B;IACxC,OAAO,CAAC,MAAM,CAAa;IAC3B,OAAO,CAAC,WAAW,CAA8B;gBAErC,MAAM,EAAE,UAAU;IAI9B;;OAEG;YACW,YAAY;IAuB1B;;OAEG;IACH,OAAO,CAAC,eAAe;IAqHvB;;OAEG;IACH,OAAO,CAAC,YAAY;IAYpB;;OAEG;IACH,OAAO,CAAC,aAAa;IAgCrB;;OAEG;IACG,IAAI,CACR,QAAQ,EAAE,OAAO,EAAE,EACnB,KAAK,CAAC,EAAE,aAAa,EAAE,EACvB,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,YAAY,CAAC;IAqBxB;;OAEG;IACI,UAAU,CACf,QAAQ,EAAE,OAAO,EAAE,EACnB,KAAK,CAAC,EAAE,aAAa,EAAE,EACvB,MAAM,CAAC,EAAE,WAAW,GACnB,cAAc,CAAC,WAAW,EAAE,IAAI,EAAE,OAAO,CAAC;IAuF7C;;OAEG;IACH,SAAS,IAAI,UAAU;IAIvB;;OAEG;IACH,YAAY,CAAC,SAAS,EAAE,OAAO,CAAC,UAAU,CAAC,GAAG,IAAI;CAGnD"}
|
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Blade 内置 Claude ChatService
|
|
3
|
+
* 完整的 ChatService 实现,API Key 从 Cloudflare Worker 获取
|
|
4
|
+
*/
|
|
5
|
+
import Anthropic from '@anthropic-ai/sdk';
|
|
6
|
+
import { fetchClaudeApiKey } from './keyService.js';
|
|
7
|
+
/**
|
|
8
|
+
* 从 data URL 或 base64 字符串中提取媒体类型
|
|
9
|
+
*/
|
|
10
|
+
function extractMediaType(url) {
|
|
11
|
+
if (url.startsWith('data:')) {
|
|
12
|
+
const match = url.match(/^data:([^;,]+)/);
|
|
13
|
+
if (match) {
|
|
14
|
+
const mediaType = match[1];
|
|
15
|
+
if (mediaType === 'image/jpeg' ||
|
|
16
|
+
mediaType === 'image/png' ||
|
|
17
|
+
mediaType === 'image/gif' ||
|
|
18
|
+
mediaType === 'image/webp') {
|
|
19
|
+
return mediaType;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return 'image/jpeg';
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* 从 data URL 中提取 base64 数据
|
|
27
|
+
*/
|
|
28
|
+
function extractBase64Data(url) {
|
|
29
|
+
if (url.startsWith('data:')) {
|
|
30
|
+
const base64Index = url.indexOf('base64,');
|
|
31
|
+
if (base64Index !== -1) {
|
|
32
|
+
return url.substring(base64Index + 7);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return url;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Blade 内置 Claude ChatService
|
|
39
|
+
*/
|
|
40
|
+
export class BladeChatService {
|
|
41
|
+
constructor(config) {
|
|
42
|
+
this.client = null;
|
|
43
|
+
this.initPromise = null;
|
|
44
|
+
this.config = config;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* 初始化客户端(懒加载)
|
|
48
|
+
*/
|
|
49
|
+
async ensureClient() {
|
|
50
|
+
if (this.client) {
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
if (this.initPromise) {
|
|
54
|
+
return this.initPromise;
|
|
55
|
+
}
|
|
56
|
+
this.initPromise = (async () => {
|
|
57
|
+
const { apiKey, baseUrl } = await fetchClaudeApiKey();
|
|
58
|
+
this.client = new Anthropic({
|
|
59
|
+
apiKey: apiKey,
|
|
60
|
+
baseURL: baseUrl,
|
|
61
|
+
timeout: this.config.timeout ?? 180000,
|
|
62
|
+
maxRetries: 3,
|
|
63
|
+
});
|
|
64
|
+
})();
|
|
65
|
+
return this.initPromise;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* 转换消息格式
|
|
69
|
+
*/
|
|
70
|
+
convertMessages(messages) {
|
|
71
|
+
let systemMessage;
|
|
72
|
+
const anthropicMessages = [];
|
|
73
|
+
for (const msg of messages) {
|
|
74
|
+
// System 消息单独处理
|
|
75
|
+
if (msg.role === 'system') {
|
|
76
|
+
systemMessage = typeof msg.content === 'string' ? msg.content : '';
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
// Tool 消息转换
|
|
80
|
+
if (msg.role === 'tool') {
|
|
81
|
+
const lastMsg = anthropicMessages[anthropicMessages.length - 1];
|
|
82
|
+
if (lastMsg && lastMsg.role === 'user') {
|
|
83
|
+
// 追加到上一个 user 消息
|
|
84
|
+
const toolResult = {
|
|
85
|
+
type: 'tool_result',
|
|
86
|
+
tool_use_id: msg.tool_call_id || '',
|
|
87
|
+
content: typeof msg.content === 'string' ? msg.content : '',
|
|
88
|
+
};
|
|
89
|
+
if (Array.isArray(lastMsg.content)) {
|
|
90
|
+
lastMsg.content.push(toolResult);
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
lastMsg.content = [
|
|
94
|
+
{ type: 'text', text: lastMsg.content },
|
|
95
|
+
toolResult,
|
|
96
|
+
];
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
// 创建新的 user 消息
|
|
101
|
+
anthropicMessages.push({
|
|
102
|
+
role: 'user',
|
|
103
|
+
content: [
|
|
104
|
+
{
|
|
105
|
+
type: 'tool_result',
|
|
106
|
+
tool_use_id: msg.tool_call_id || '',
|
|
107
|
+
content: typeof msg.content === 'string' ? msg.content : '',
|
|
108
|
+
},
|
|
109
|
+
],
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
// Assistant 消息处理 tool_calls
|
|
115
|
+
if (msg.role === 'assistant' && msg.tool_calls && msg.tool_calls.length > 0) {
|
|
116
|
+
const content = [];
|
|
117
|
+
// 添加文本内容
|
|
118
|
+
if (typeof msg.content === 'string' && msg.content) {
|
|
119
|
+
content.push({
|
|
120
|
+
type: 'text',
|
|
121
|
+
text: msg.content,
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
// 添加 tool_use
|
|
125
|
+
for (const toolCall of msg.tool_calls) {
|
|
126
|
+
content.push({
|
|
127
|
+
type: 'tool_use',
|
|
128
|
+
id: toolCall.id,
|
|
129
|
+
name: toolCall.function.name,
|
|
130
|
+
input: JSON.parse(toolCall.function.arguments),
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
anthropicMessages.push({
|
|
134
|
+
role: 'assistant',
|
|
135
|
+
content,
|
|
136
|
+
});
|
|
137
|
+
continue;
|
|
138
|
+
}
|
|
139
|
+
// 普通消息处理
|
|
140
|
+
if (typeof msg.content === 'string') {
|
|
141
|
+
anthropicMessages.push({
|
|
142
|
+
role: msg.role,
|
|
143
|
+
content: msg.content,
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
else {
|
|
147
|
+
// 多模态内容
|
|
148
|
+
const content = [];
|
|
149
|
+
for (const part of msg.content) {
|
|
150
|
+
if (part.type === 'text') {
|
|
151
|
+
content.push({
|
|
152
|
+
type: 'text',
|
|
153
|
+
text: part.text,
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
else if (part.type === 'image_url') {
|
|
157
|
+
const url = part.image_url.url;
|
|
158
|
+
content.push({
|
|
159
|
+
type: 'image',
|
|
160
|
+
source: {
|
|
161
|
+
type: 'base64',
|
|
162
|
+
media_type: extractMediaType(url),
|
|
163
|
+
data: extractBase64Data(url),
|
|
164
|
+
},
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
anthropicMessages.push({
|
|
169
|
+
role: msg.role,
|
|
170
|
+
content,
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
return { system: systemMessage, messages: anthropicMessages };
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* 转换工具格式
|
|
178
|
+
*/
|
|
179
|
+
convertTools(tools) {
|
|
180
|
+
if (!tools || tools.length === 0) {
|
|
181
|
+
return undefined;
|
|
182
|
+
}
|
|
183
|
+
return tools.map(tool => ({
|
|
184
|
+
name: tool.name,
|
|
185
|
+
description: tool.description,
|
|
186
|
+
input_schema: tool.parameters,
|
|
187
|
+
}));
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* 解析响应
|
|
191
|
+
*/
|
|
192
|
+
parseResponse(response) {
|
|
193
|
+
let content = '';
|
|
194
|
+
const toolCalls = [];
|
|
195
|
+
for (const block of response.content) {
|
|
196
|
+
if (block.type === 'text') {
|
|
197
|
+
content += block.text;
|
|
198
|
+
}
|
|
199
|
+
else if (block.type === 'tool_use') {
|
|
200
|
+
toolCalls.push({
|
|
201
|
+
id: block.id,
|
|
202
|
+
type: 'function',
|
|
203
|
+
function: {
|
|
204
|
+
name: block.name,
|
|
205
|
+
arguments: JSON.stringify(block.input),
|
|
206
|
+
},
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
const usage = {
|
|
211
|
+
promptTokens: response.usage.input_tokens,
|
|
212
|
+
completionTokens: response.usage.output_tokens,
|
|
213
|
+
totalTokens: response.usage.input_tokens + response.usage.output_tokens,
|
|
214
|
+
};
|
|
215
|
+
return {
|
|
216
|
+
content,
|
|
217
|
+
toolCalls: toolCalls.length > 0 ? toolCalls : undefined,
|
|
218
|
+
usage,
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* 发送聊天请求(非流式)
|
|
223
|
+
*/
|
|
224
|
+
async chat(messages, tools, signal) {
|
|
225
|
+
await this.ensureClient();
|
|
226
|
+
const { system, messages: anthropicMessages } = this.convertMessages(messages);
|
|
227
|
+
const anthropicTools = this.convertTools(tools);
|
|
228
|
+
const response = await this.client.messages.create({
|
|
229
|
+
model: this.config.model,
|
|
230
|
+
max_tokens: this.config.maxOutputTokens || 4096,
|
|
231
|
+
temperature: this.config.temperature,
|
|
232
|
+
system,
|
|
233
|
+
messages: anthropicMessages,
|
|
234
|
+
tools: anthropicTools,
|
|
235
|
+
}, { signal });
|
|
236
|
+
return this.parseResponse(response);
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* 发送聊天请求(流式)
|
|
240
|
+
*/
|
|
241
|
+
async *streamChat(messages, tools, signal) {
|
|
242
|
+
await this.ensureClient();
|
|
243
|
+
const { system, messages: anthropicMessages } = this.convertMessages(messages);
|
|
244
|
+
const anthropicTools = this.convertTools(tools);
|
|
245
|
+
const stream = await this.client.messages.create({
|
|
246
|
+
model: this.config.model,
|
|
247
|
+
max_tokens: this.config.maxOutputTokens || 4096,
|
|
248
|
+
temperature: this.config.temperature,
|
|
249
|
+
system,
|
|
250
|
+
messages: anthropicMessages,
|
|
251
|
+
tools: anthropicTools,
|
|
252
|
+
stream: true,
|
|
253
|
+
}, { signal });
|
|
254
|
+
// 累积 tool_use 的参数
|
|
255
|
+
const toolCallsMap = new Map();
|
|
256
|
+
for await (const event of stream) {
|
|
257
|
+
if (event.type === 'content_block_start') {
|
|
258
|
+
if (event.content_block.type === 'tool_use') {
|
|
259
|
+
toolCallsMap.set(event.index, {
|
|
260
|
+
id: event.content_block.id,
|
|
261
|
+
name: event.content_block.name,
|
|
262
|
+
arguments: '',
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
else if (event.type === 'content_block_delta') {
|
|
267
|
+
if (event.delta.type === 'text_delta') {
|
|
268
|
+
yield {
|
|
269
|
+
content: event.delta.text,
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
else if (event.delta.type === 'input_json_delta') {
|
|
273
|
+
const toolCall = toolCallsMap.get(event.index);
|
|
274
|
+
if (toolCall) {
|
|
275
|
+
toolCall.arguments += event.delta.partial_json;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
else if (event.type === 'message_delta') {
|
|
280
|
+
if (event.delta.stop_reason) {
|
|
281
|
+
// 发送完整的 tool_calls
|
|
282
|
+
if (toolCallsMap.size > 0) {
|
|
283
|
+
const toolCalls = Array.from(toolCallsMap.values()).map(tc => ({
|
|
284
|
+
id: tc.id,
|
|
285
|
+
type: 'function',
|
|
286
|
+
function: {
|
|
287
|
+
name: tc.name,
|
|
288
|
+
arguments: tc.arguments,
|
|
289
|
+
},
|
|
290
|
+
}));
|
|
291
|
+
yield {
|
|
292
|
+
toolCalls,
|
|
293
|
+
finishReason: event.delta.stop_reason,
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
else {
|
|
297
|
+
yield {
|
|
298
|
+
finishReason: event.delta.stop_reason,
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
if (event.usage) {
|
|
303
|
+
yield {
|
|
304
|
+
usage: {
|
|
305
|
+
promptTokens: event.usage.input_tokens || 0,
|
|
306
|
+
completionTokens: event.usage.output_tokens || 0,
|
|
307
|
+
totalTokens: (event.usage.input_tokens || 0) + (event.usage.output_tokens || 0),
|
|
308
|
+
},
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
/**
|
|
315
|
+
* 获取配置
|
|
316
|
+
*/
|
|
317
|
+
getConfig() {
|
|
318
|
+
return this.config;
|
|
319
|
+
}
|
|
320
|
+
/**
|
|
321
|
+
* 更新配置
|
|
322
|
+
*/
|
|
323
|
+
updateConfig(newConfig) {
|
|
324
|
+
this.config = { ...this.config, ...newConfig };
|
|
325
|
+
}
|
|
326
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @blade/auth-service
|
|
3
|
+
* Blade 私有认证服务 - 提供完整的 Claude ChatService 实现
|
|
4
|
+
*/
|
|
5
|
+
export { BladeChatService } from './chatService.js';
|
|
6
|
+
export { fetchClaudeApiKey, clearKeyCache } from './keyService.js';
|
|
7
|
+
export type { Message, Tool, ChatConfig, ChatResponse, StreamChunk, UsageInfo, ContentPart, MessageRole, } from './types.js';
|
|
8
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AACnE,YAAY,EACV,OAAO,EACP,IAAI,EACJ,UAAU,EACV,YAAY,EACZ,WAAW,EACX,SAAS,EACT,WAAW,EACX,WAAW,GACZ,MAAM,YAAY,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* API Key 获取服务
|
|
3
|
+
* 从 Cloudflare Worker 获取真实的 Claude API Key
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* 从 Worker 获取 Claude API Key
|
|
7
|
+
*/
|
|
8
|
+
export declare function fetchClaudeApiKey(): Promise<{
|
|
9
|
+
apiKey: string;
|
|
10
|
+
baseUrl: string;
|
|
11
|
+
}>;
|
|
12
|
+
/**
|
|
13
|
+
* 清除缓存
|
|
14
|
+
*/
|
|
15
|
+
export declare function clearKeyCache(): void;
|
|
16
|
+
//# sourceMappingURL=keyService.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"keyService.d.ts","sourceRoot":"","sources":["../src/keyService.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAgBH;;GAEG;AACH,wBAAsB,iBAAiB,IAAI,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC,CAmCtF;AAED;;GAEG;AACH,wBAAgB,aAAa,IAAI,IAAI,CAGpC"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* API Key 获取服务
|
|
3
|
+
* 从 Cloudflare Worker 获取真实的 Claude API Key
|
|
4
|
+
*/
|
|
5
|
+
const WORKER_URL = 'https://blade-api-proxy.137844255.workers.dev/v1/get-claude-key';
|
|
6
|
+
const BUILTIN_TOKEN = 'blade-free-tier';
|
|
7
|
+
let cachedApiKey = null;
|
|
8
|
+
let cachedBaseUrl = null;
|
|
9
|
+
/**
|
|
10
|
+
* 从 Worker 获取 Claude API Key
|
|
11
|
+
*/
|
|
12
|
+
export async function fetchClaudeApiKey() {
|
|
13
|
+
// 使用缓存
|
|
14
|
+
if (cachedApiKey && cachedBaseUrl) {
|
|
15
|
+
return { apiKey: cachedApiKey, baseUrl: cachedBaseUrl };
|
|
16
|
+
}
|
|
17
|
+
try {
|
|
18
|
+
const response = await fetch(WORKER_URL, {
|
|
19
|
+
method: 'GET',
|
|
20
|
+
headers: {
|
|
21
|
+
'Authorization': `Bearer ${BUILTIN_TOKEN}`,
|
|
22
|
+
'Content-Type': 'application/json',
|
|
23
|
+
},
|
|
24
|
+
});
|
|
25
|
+
if (!response.ok) {
|
|
26
|
+
const errorText = await response.text();
|
|
27
|
+
throw new Error(`获取 API Key 失败: ${response.status} - ${errorText}`);
|
|
28
|
+
}
|
|
29
|
+
const data = await response.json();
|
|
30
|
+
if (!data.apiKey) {
|
|
31
|
+
throw new Error('Worker 返回的数据中没有 apiKey');
|
|
32
|
+
}
|
|
33
|
+
cachedApiKey = data.apiKey;
|
|
34
|
+
cachedBaseUrl = data.baseUrl || 'https://api.anthropic.com';
|
|
35
|
+
return { apiKey: cachedApiKey, baseUrl: cachedBaseUrl };
|
|
36
|
+
}
|
|
37
|
+
catch (error) {
|
|
38
|
+
throw new Error(`无法获取 Claude API Key: ${error instanceof Error ? error.message : '未知错误'}`);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* 清除缓存
|
|
43
|
+
*/
|
|
44
|
+
export function clearKeyCache() {
|
|
45
|
+
cachedApiKey = null;
|
|
46
|
+
cachedBaseUrl = null;
|
|
47
|
+
}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 类型定义
|
|
3
|
+
*/
|
|
4
|
+
export type MessageRole = 'user' | 'assistant' | 'system' | 'tool';
|
|
5
|
+
export interface TextContentPart {
|
|
6
|
+
type: 'text';
|
|
7
|
+
text: string;
|
|
8
|
+
}
|
|
9
|
+
export interface ImageContentPart {
|
|
10
|
+
type: 'image_url';
|
|
11
|
+
image_url: {
|
|
12
|
+
url: string;
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
export type ContentPart = TextContentPart | ImageContentPart;
|
|
16
|
+
export interface Message {
|
|
17
|
+
role: MessageRole;
|
|
18
|
+
content: string | ContentPart[];
|
|
19
|
+
reasoningContent?: string;
|
|
20
|
+
tool_call_id?: string;
|
|
21
|
+
name?: string;
|
|
22
|
+
tool_calls?: Array<{
|
|
23
|
+
id: string;
|
|
24
|
+
type: 'function';
|
|
25
|
+
function: {
|
|
26
|
+
name: string;
|
|
27
|
+
arguments: string;
|
|
28
|
+
};
|
|
29
|
+
}>;
|
|
30
|
+
}
|
|
31
|
+
export interface Tool {
|
|
32
|
+
name: string;
|
|
33
|
+
description: string;
|
|
34
|
+
parameters: unknown;
|
|
35
|
+
}
|
|
36
|
+
export interface UsageInfo {
|
|
37
|
+
promptTokens: number;
|
|
38
|
+
completionTokens: number;
|
|
39
|
+
totalTokens: number;
|
|
40
|
+
reasoningTokens?: number;
|
|
41
|
+
}
|
|
42
|
+
export interface ChatResponse {
|
|
43
|
+
content: string;
|
|
44
|
+
reasoningContent?: string;
|
|
45
|
+
toolCalls?: Array<{
|
|
46
|
+
id: string;
|
|
47
|
+
type: 'function';
|
|
48
|
+
function: {
|
|
49
|
+
name: string;
|
|
50
|
+
arguments: string;
|
|
51
|
+
};
|
|
52
|
+
}>;
|
|
53
|
+
usage?: UsageInfo;
|
|
54
|
+
}
|
|
55
|
+
export interface StreamChunk {
|
|
56
|
+
content?: string;
|
|
57
|
+
reasoningContent?: string;
|
|
58
|
+
toolCalls?: Array<{
|
|
59
|
+
id?: string;
|
|
60
|
+
type?: 'function';
|
|
61
|
+
function?: {
|
|
62
|
+
name?: string;
|
|
63
|
+
arguments?: string;
|
|
64
|
+
};
|
|
65
|
+
}>;
|
|
66
|
+
finishReason?: string;
|
|
67
|
+
usage?: UsageInfo;
|
|
68
|
+
}
|
|
69
|
+
export interface ChatConfig {
|
|
70
|
+
model: string;
|
|
71
|
+
temperature?: number;
|
|
72
|
+
maxContextTokens?: number;
|
|
73
|
+
maxOutputTokens?: number;
|
|
74
|
+
timeout?: number;
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,WAAW,GAAG,QAAQ,GAAG,MAAM,CAAC;AAEnE,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,WAAW,CAAC;IAClB,SAAS,EAAE;QACT,GAAG,EAAE,MAAM,CAAC;KACb,CAAC;CACH;AAED,MAAM,MAAM,WAAW,GAAG,eAAe,GAAG,gBAAgB,CAAC;AAE7D,MAAM,WAAW,OAAO;IACtB,IAAI,EAAE,WAAW,CAAC;IAClB,OAAO,EAAE,MAAM,GAAG,WAAW,EAAE,CAAC;IAChC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,KAAK,CAAC;QACjB,EAAE,EAAE,MAAM,CAAC;QACX,IAAI,EAAE,UAAU,CAAC;QACjB,QAAQ,EAAE;YACR,IAAI,EAAE,MAAM,CAAC;YACb,SAAS,EAAE,MAAM,CAAC;SACnB,CAAC;KACH,CAAC,CAAC;CACJ;AAED,MAAM,WAAW,IAAI;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,OAAO,CAAC;CACrB;AAED,MAAM,WAAW,SAAS;IACxB,YAAY,EAAE,MAAM,CAAC;IACrB,gBAAgB,EAAE,MAAM,CAAC;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,SAAS,CAAC,EAAE,KAAK,CAAC;QAChB,EAAE,EAAE,MAAM,CAAC;QACX,IAAI,EAAE,UAAU,CAAC;QACjB,QAAQ,EAAE;YACR,IAAI,EAAE,MAAM,CAAC;YACb,SAAS,EAAE,MAAM,CAAC;SACnB,CAAC;KACH,CAAC,CAAC;IACH,KAAK,CAAC,EAAE,SAAS,CAAC;CACnB;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,SAAS,CAAC,EAAE,KAAK,CAAC;QAChB,EAAE,CAAC,EAAE,MAAM,CAAC;QACZ,IAAI,CAAC,EAAE,UAAU,CAAC;QAClB,QAAQ,CAAC,EAAE;YACT,IAAI,CAAC,EAAE,MAAM,CAAC;YACd,SAAS,CAAC,EAAE,MAAM,CAAC;SACpB,CAAC;KACH,CAAC,CAAC;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,CAAC,EAAE,SAAS,CAAC;CACnB;AAED,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB"}
|
package/dist/types.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "blade-auth-service",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Blade 私有认证服务 - 提供完整的 Claude ChatService 实现",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"files": [
|
|
9
|
+
"dist"
|
|
10
|
+
],
|
|
11
|
+
"scripts": {
|
|
12
|
+
"build": "tsc",
|
|
13
|
+
"dev": "tsc --watch",
|
|
14
|
+
"prepublishOnly": "npm run build"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"blade",
|
|
18
|
+
"claude",
|
|
19
|
+
"anthropic",
|
|
20
|
+
"chatservice"
|
|
21
|
+
],
|
|
22
|
+
"author": "Blade Team",
|
|
23
|
+
"license": "PRIVATE",
|
|
24
|
+
"repository": {
|
|
25
|
+
"type": "git",
|
|
26
|
+
"url": "git+https://github.com/echoVic/blade-auth-service.git"
|
|
27
|
+
},
|
|
28
|
+
"dependencies": {
|
|
29
|
+
"@anthropic-ai/sdk": "^0.71.2"
|
|
30
|
+
},
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"@types/node": "^20.0.0",
|
|
33
|
+
"typescript": "^5.0.0"
|
|
34
|
+
}
|
|
35
|
+
}
|