@zhin.js/core 1.0.23 → 1.0.25
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 +21 -0
- package/lib/adapter.d.ts +28 -1
- package/lib/adapter.d.ts.map +1 -1
- package/lib/adapter.js +100 -1
- package/lib/adapter.js.map +1 -1
- package/lib/built/adapter-process.d.ts +4 -0
- package/lib/built/adapter-process.d.ts.map +1 -1
- package/lib/built/adapter-process.js +94 -0
- package/lib/built/adapter-process.js.map +1 -1
- package/lib/built/ai-trigger.d.ts +89 -0
- package/lib/built/ai-trigger.d.ts.map +1 -0
- package/lib/built/ai-trigger.js +162 -0
- package/lib/built/ai-trigger.js.map +1 -0
- package/lib/built/component.d.ts.map +1 -1
- package/lib/built/component.js +0 -1
- package/lib/built/component.js.map +1 -1
- package/lib/built/database.d.ts.map +1 -1
- package/lib/built/database.js +2 -2
- package/lib/built/database.js.map +1 -1
- package/lib/built/tool.d.ts +281 -0
- package/lib/built/tool.d.ts.map +1 -0
- package/lib/built/tool.js +834 -0
- package/lib/built/tool.js.map +1 -0
- package/lib/index.d.ts +2 -0
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +4 -0
- package/lib/index.js.map +1 -1
- package/lib/plugin.d.ts +28 -1
- package/lib/plugin.d.ts.map +1 -1
- package/lib/plugin.js +124 -14
- package/lib/plugin.js.map +1 -1
- package/lib/types.d.ts +244 -0
- package/lib/types.d.ts.map +1 -1
- package/lib/utils.d.ts.map +1 -1
- package/lib/utils.js +38 -12
- package/lib/utils.js.map +1 -1
- package/package.json +4 -4
- package/src/adapter.ts +107 -2
- package/src/built/adapter-process.ts +99 -0
- package/src/built/ai-trigger.ts +256 -0
- package/src/built/component.ts +0 -1
- package/src/built/database.ts +2 -2
- package/src/built/tool.ts +1049 -0
- package/src/index.ts +4 -0
- package/src/plugin.ts +164 -39
- package/src/types.ts +300 -1
- package/src/utils.ts +37 -13
- package/tests/component-new.test.ts +17 -6
|
@@ -0,0 +1,1049 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool Service
|
|
3
|
+
* 统一的工具管理服务,支持 Tool ↔ Command 互转
|
|
4
|
+
*/
|
|
5
|
+
import { MessageCommand } from "../command.js";
|
|
6
|
+
import { Message } from "../message.js";
|
|
7
|
+
import { Context, Plugin, getPlugin } from "../plugin.js";
|
|
8
|
+
import type { Tool, ToolDefinition, RegisteredAdapter, AdapterMessage, ToolContext, ToolJsonSchema, ToolParametersSchema, PropertySchema, MaybePromise, ToolPermissionLevel, ToolScope } from "../types.js";
|
|
9
|
+
import { MatchResult } from "segment-matcher";
|
|
10
|
+
|
|
11
|
+
// ============================================================================
|
|
12
|
+
// 权限级别比较
|
|
13
|
+
// ============================================================================
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* 权限级别优先级(数字越大权限越高)
|
|
17
|
+
*/
|
|
18
|
+
const PERMISSION_LEVEL_PRIORITY: Record<ToolPermissionLevel, number> = {
|
|
19
|
+
'user': 0,
|
|
20
|
+
'group_admin': 1,
|
|
21
|
+
'group_owner': 2,
|
|
22
|
+
'bot_admin': 3,
|
|
23
|
+
'owner': 4,
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* 比较两个权限级别
|
|
28
|
+
* @returns 如果 a >= b 返回 true
|
|
29
|
+
*/
|
|
30
|
+
function hasPermissionLevel(userLevel: ToolPermissionLevel, requiredLevel: ToolPermissionLevel): boolean {
|
|
31
|
+
return PERMISSION_LEVEL_PRIORITY[userLevel] >= PERMISSION_LEVEL_PRIORITY[requiredLevel];
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* 从 ToolContext 推断用户的权限级别
|
|
36
|
+
*/
|
|
37
|
+
function inferPermissionLevel(context: ToolContext): ToolPermissionLevel {
|
|
38
|
+
if (context.senderPermissionLevel) {
|
|
39
|
+
return context.senderPermissionLevel;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// 按优先级检查
|
|
43
|
+
if (context.isOwner) return 'owner';
|
|
44
|
+
if (context.isBotAdmin) return 'bot_admin';
|
|
45
|
+
if (context.isGroupOwner) return 'group_owner';
|
|
46
|
+
if (context.isGroupAdmin) return 'group_admin';
|
|
47
|
+
|
|
48
|
+
return 'user';
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* 检查工具是否可被当前上下文访问
|
|
53
|
+
*/
|
|
54
|
+
function canAccessTool(tool: Tool, context: ToolContext): boolean {
|
|
55
|
+
// 1. 检查平台限制
|
|
56
|
+
if (tool.platforms && tool.platforms.length > 0) {
|
|
57
|
+
if (!context.platform || !tool.platforms.includes(context.platform)) {
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// 2. 检查场景限制
|
|
63
|
+
if (tool.scopes && tool.scopes.length > 0) {
|
|
64
|
+
if (!context.scope || !tool.scopes.includes(context.scope)) {
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// 3. 检查权限级别
|
|
70
|
+
const requiredLevel = tool.permissionLevel || 'user';
|
|
71
|
+
const userLevel = inferPermissionLevel(context);
|
|
72
|
+
|
|
73
|
+
if (!hasPermissionLevel(userLevel, requiredLevel)) {
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return true;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// ============================================================================
|
|
81
|
+
// Tool 工具函数
|
|
82
|
+
// ============================================================================
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* 从 Tool 参数生成命令模式
|
|
86
|
+
* @example
|
|
87
|
+
* parameters: { properties: { city: { type: 'string' } }, required: ['city'] }
|
|
88
|
+
* => 'toolName <city>'
|
|
89
|
+
*/
|
|
90
|
+
export function generatePattern(tool: Tool): string {
|
|
91
|
+
const { name, parameters } = tool;
|
|
92
|
+
|
|
93
|
+
if (tool.command && tool.command.pattern) {
|
|
94
|
+
return tool.command.pattern;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (!parameters.properties) {
|
|
98
|
+
return name;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const parts: string[] = [name];
|
|
102
|
+
const props = parameters.properties;
|
|
103
|
+
const required = parameters.required || [];
|
|
104
|
+
|
|
105
|
+
// 按照 required 优先、字母顺序排序
|
|
106
|
+
const sortedKeys = Object.keys(props).sort((a, b) => {
|
|
107
|
+
const aReq = required.includes(a) ? 0 : 1;
|
|
108
|
+
const bReq = required.includes(b) ? 0 : 1;
|
|
109
|
+
if (aReq !== bReq) return aReq - bReq;
|
|
110
|
+
return a.localeCompare(b);
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
for (const key of sortedKeys) {
|
|
114
|
+
const prop = props[key];
|
|
115
|
+
const isRequired = required.includes(key);
|
|
116
|
+
const paramType = prop.paramType || (prop.type === 'number' ? 'number' : 'text');
|
|
117
|
+
|
|
118
|
+
if (isRequired) {
|
|
119
|
+
parts.push(`<${key}:${paramType}>`);
|
|
120
|
+
} else {
|
|
121
|
+
parts.push(`[${key}:${paramType}]`);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return parts.join(' ');
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* 从参数定义中提取参数信息
|
|
130
|
+
*/
|
|
131
|
+
export function extractParamInfo(parameters: ToolJsonSchema): Tool.ParamInfo[] {
|
|
132
|
+
if (!parameters.properties) return [];
|
|
133
|
+
|
|
134
|
+
const required = parameters.required || [];
|
|
135
|
+
return Object.entries(parameters.properties).map(([name, schema]) => ({
|
|
136
|
+
name,
|
|
137
|
+
type: schema.type,
|
|
138
|
+
required: required.includes(name),
|
|
139
|
+
description: schema.description,
|
|
140
|
+
default: schema.default,
|
|
141
|
+
enum: schema.enum,
|
|
142
|
+
}));
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* 定义工具的辅助函数(提供类型推断)
|
|
147
|
+
*
|
|
148
|
+
* 使用泛型参数获得 execute 函数的类型检查,
|
|
149
|
+
* 返回通用的 Tool 类型,可直接传给 addTool
|
|
150
|
+
*
|
|
151
|
+
* @example
|
|
152
|
+
* ```typescript
|
|
153
|
+
* const myTool = defineTool<{ name: string }>({
|
|
154
|
+
* name: 'greet',
|
|
155
|
+
* description: '打招呼',
|
|
156
|
+
* parameters: {
|
|
157
|
+
* type: 'object',
|
|
158
|
+
* properties: {
|
|
159
|
+
* name: { type: 'string', description: '名字' }
|
|
160
|
+
* },
|
|
161
|
+
* required: ['name']
|
|
162
|
+
* },
|
|
163
|
+
* execute: async (args) => {
|
|
164
|
+
* return `你好,${args.name}!`; // args.name 有类型提示
|
|
165
|
+
* }
|
|
166
|
+
* });
|
|
167
|
+
*
|
|
168
|
+
* plugin.addTool(myTool); // 无需类型断言
|
|
169
|
+
* ```
|
|
170
|
+
*/
|
|
171
|
+
export function defineTool<TArgs extends Record<string, any> = Record<string, any>>(
|
|
172
|
+
tool: ToolDefinition<TArgs>
|
|
173
|
+
): Tool {
|
|
174
|
+
// ToolDefinition<TArgs> 兼容 Tool,因为 execute 参数是协变的
|
|
175
|
+
return tool as Tool;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// ============================================================================
|
|
179
|
+
// ZhinTool 类(链式调用风格)
|
|
180
|
+
// ============================================================================
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* 参数定义(带顺序)
|
|
184
|
+
*/
|
|
185
|
+
interface ParamDef {
|
|
186
|
+
name: string;
|
|
187
|
+
schema: PropertySchema;
|
|
188
|
+
required: boolean;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* ZhinTool 类
|
|
193
|
+
* 提供类似 MessageCommand 的链式调用风格来定义工具
|
|
194
|
+
*
|
|
195
|
+
* @example
|
|
196
|
+
* ```typescript
|
|
197
|
+
* const weatherTool = new ZhinTool('weather')
|
|
198
|
+
* .desc('查询天气信息')
|
|
199
|
+
* .param('city', { type: 'string', description: '城市名称' }, true)
|
|
200
|
+
* .param('days', { type: 'number', description: '预报天数' })
|
|
201
|
+
* .platform('qq', 'telegram')
|
|
202
|
+
* .scope('group', 'private')
|
|
203
|
+
* .permission('user')
|
|
204
|
+
* // AI 调用时执行(必须)
|
|
205
|
+
* .execute(async (args, ctx) => {
|
|
206
|
+
* return `${args.city} 的天气是晴天`;
|
|
207
|
+
* })
|
|
208
|
+
* // 可选:命令回调(与 MessageCommand.action 一致)
|
|
209
|
+
* // 如果定义了此回调,会生成 Command
|
|
210
|
+
* .action(async (message, result) => {
|
|
211
|
+
* return `命令执行: ${result.params.city}`;
|
|
212
|
+
* });
|
|
213
|
+
*
|
|
214
|
+
* plugin.addTool(weatherTool);
|
|
215
|
+
* ```
|
|
216
|
+
*/
|
|
217
|
+
export class ZhinTool {
|
|
218
|
+
#name: string;
|
|
219
|
+
#description: string = '';
|
|
220
|
+
/** 有序的参数列表 */
|
|
221
|
+
#params: ParamDef[] = [];
|
|
222
|
+
#execute?: (args: Record<string, any>, context?: ToolContext) => MaybePromise<any>;
|
|
223
|
+
#platforms: string[] = [];
|
|
224
|
+
#scopes: ToolScope[] = [];
|
|
225
|
+
#permissionLevel: ToolPermissionLevel = 'user';
|
|
226
|
+
#permissions: string[] = [];
|
|
227
|
+
#tags: string[] = [];
|
|
228
|
+
/** 命令回调(入参是 message, matchResult) */
|
|
229
|
+
#commandCallback?: MessageCommand.Callback<RegisteredAdapter>;
|
|
230
|
+
/** 命令配置 */
|
|
231
|
+
#commandConfig: Omit<Tool.CommandConfig, 'enabled'> = {};
|
|
232
|
+
#hidden: boolean = false;
|
|
233
|
+
#source?: string;
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* 创建工具实例
|
|
237
|
+
* @param name 工具名称(唯一标识,建议使用 snake_case)
|
|
238
|
+
*/
|
|
239
|
+
constructor(name: string) {
|
|
240
|
+
this.#name = name;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/** 获取工具名称 */
|
|
244
|
+
get name(): string {
|
|
245
|
+
return this.#name;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/** 获取工具描述 */
|
|
249
|
+
get description(): string {
|
|
250
|
+
return this.#description;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/** 获取有序的参数列表 */
|
|
254
|
+
get params(): ParamDef[] {
|
|
255
|
+
return [...this.#params];
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* 设置工具描述
|
|
260
|
+
* @param description 工具描述(供 AI 和帮助系统使用)
|
|
261
|
+
*/
|
|
262
|
+
desc(description: string): this {
|
|
263
|
+
this.#description = description;
|
|
264
|
+
return this;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* 添加参数(按调用顺序保持有序)
|
|
269
|
+
* @param name 参数名称
|
|
270
|
+
* @param schema 参数 Schema(类型、描述等)
|
|
271
|
+
* @param required 是否必填(默认 false)
|
|
272
|
+
*/
|
|
273
|
+
param(name: string, schema: PropertySchema, required: boolean = false): this {
|
|
274
|
+
// 如果已存在同名参数,更新它
|
|
275
|
+
const existingIndex = this.#params.findIndex(p => p.name === name);
|
|
276
|
+
if (existingIndex >= 0) {
|
|
277
|
+
this.#params[existingIndex] = { name, schema, required };
|
|
278
|
+
} else {
|
|
279
|
+
this.#params.push({ name, schema, required });
|
|
280
|
+
}
|
|
281
|
+
return this;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* 设置支持的平台
|
|
286
|
+
* @param platforms 平台名称列表(如 'qq', 'telegram')
|
|
287
|
+
*/
|
|
288
|
+
platform(...platforms: string[]): this {
|
|
289
|
+
this.#platforms.push(...platforms);
|
|
290
|
+
return this;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* 设置支持的场景
|
|
295
|
+
* @param scopes 场景类型列表('private', 'group', 'channel')
|
|
296
|
+
*/
|
|
297
|
+
scope(...scopes: ToolScope[]): this {
|
|
298
|
+
this.#scopes.push(...scopes);
|
|
299
|
+
return this;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* 设置权限级别
|
|
304
|
+
* @param level 权限级别('user', 'group_admin', 'group_owner', 'bot_admin', 'owner')
|
|
305
|
+
*/
|
|
306
|
+
permission(level: ToolPermissionLevel): this {
|
|
307
|
+
this.#permissionLevel = level;
|
|
308
|
+
return this;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* 添加旧版权限要求(兼容 MessageCommand)
|
|
313
|
+
* @param permissions 权限字符串列表
|
|
314
|
+
*/
|
|
315
|
+
permit(...permissions: string[]): this {
|
|
316
|
+
this.#permissions.push(...permissions);
|
|
317
|
+
return this;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
/**
|
|
321
|
+
* 添加标签
|
|
322
|
+
* @param tags 标签列表
|
|
323
|
+
*/
|
|
324
|
+
tag(...tags: string[]): this {
|
|
325
|
+
this.#tags.push(...tags);
|
|
326
|
+
return this;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* 设置是否隐藏
|
|
331
|
+
* @param value 是否隐藏(默认 true)
|
|
332
|
+
*/
|
|
333
|
+
hidden(value: boolean = true): this {
|
|
334
|
+
this.#hidden = value;
|
|
335
|
+
return this;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/**
|
|
339
|
+
* 设置使用说明(命令配置)
|
|
340
|
+
* @param usage 使用说明列表
|
|
341
|
+
*/
|
|
342
|
+
usage(...usage: string[]): this {
|
|
343
|
+
this.#commandConfig.usage = [...(this.#commandConfig.usage || []), ...usage];
|
|
344
|
+
return this;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* 设置示例(命令配置)
|
|
349
|
+
* @param examples 示例列表
|
|
350
|
+
*/
|
|
351
|
+
examples(...examples: string[]): this {
|
|
352
|
+
this.#commandConfig.examples = [...(this.#commandConfig.examples || []), ...examples];
|
|
353
|
+
return this;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
/**
|
|
357
|
+
* 设置别名(命令配置)
|
|
358
|
+
* @param alias 别名列表
|
|
359
|
+
*/
|
|
360
|
+
alias(...alias: string[]): this {
|
|
361
|
+
this.#commandConfig.alias = [...(this.#commandConfig.alias || []), ...alias];
|
|
362
|
+
return this;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
/**
|
|
366
|
+
* 设置自定义命令模式
|
|
367
|
+
* @param pattern 命令模式(如 'weather <city> [days]')
|
|
368
|
+
*/
|
|
369
|
+
pattern(pattern: string): this {
|
|
370
|
+
this.#commandConfig.pattern = pattern;
|
|
371
|
+
return this;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
/**
|
|
375
|
+
* 设置 AI 工具执行函数
|
|
376
|
+
* @param callback 执行回调,入参是 (args, context)
|
|
377
|
+
*/
|
|
378
|
+
execute(callback: (args: Record<string, any>, context?: ToolContext) => MaybePromise<any>): this {
|
|
379
|
+
this.#execute = callback;
|
|
380
|
+
return this;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
/**
|
|
384
|
+
* 设置命令回调(可选,与 MessageCommand.action 一致)
|
|
385
|
+
* 如果定义了此回调,会自动生成 Command
|
|
386
|
+
* 入参是 (message, matchResult)
|
|
387
|
+
*
|
|
388
|
+
* @param callback 命令回调
|
|
389
|
+
*/
|
|
390
|
+
action(callback: MessageCommand.Callback<RegisteredAdapter>): this {
|
|
391
|
+
this.#commandCallback = callback;
|
|
392
|
+
return this;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
/**
|
|
396
|
+
* 构建有序的 ToolParametersSchema
|
|
397
|
+
*/
|
|
398
|
+
#buildParameters(): ToolParametersSchema {
|
|
399
|
+
const properties: Record<string, PropertySchema> = {};
|
|
400
|
+
const required: string[] = [];
|
|
401
|
+
|
|
402
|
+
// 按顺序构建(必填参数排前面)
|
|
403
|
+
const sortedParams = [...this.#params].sort((a, b) => {
|
|
404
|
+
if (a.required && !b.required) return -1;
|
|
405
|
+
if (!a.required && b.required) return 1;
|
|
406
|
+
return 0;
|
|
407
|
+
});
|
|
408
|
+
|
|
409
|
+
for (const param of sortedParams) {
|
|
410
|
+
properties[param.name] = param.schema;
|
|
411
|
+
if (param.required) {
|
|
412
|
+
required.push(param.name);
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
return {
|
|
417
|
+
type: 'object',
|
|
418
|
+
properties,
|
|
419
|
+
required: required.length > 0 ? required : undefined,
|
|
420
|
+
};
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
/**
|
|
424
|
+
* 生成命令模式字符串
|
|
425
|
+
*/
|
|
426
|
+
#generatePattern(): string {
|
|
427
|
+
if (this.#commandConfig.pattern) {
|
|
428
|
+
return this.#commandConfig.pattern;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
const parts: string[] = [this.#name];
|
|
432
|
+
|
|
433
|
+
// 按顺序生成参数(必填在前,可选在后)
|
|
434
|
+
const sortedParams = [...this.#params].sort((a, b) => {
|
|
435
|
+
if (a.required && !b.required) return -1;
|
|
436
|
+
if (!a.required && b.required) return 1;
|
|
437
|
+
return 0;
|
|
438
|
+
});
|
|
439
|
+
|
|
440
|
+
for (const param of sortedParams) {
|
|
441
|
+
const paramType = param.schema.paramType || (param.schema.type === 'number' ? 'number' : 'text');
|
|
442
|
+
if (param.required) {
|
|
443
|
+
parts.push(`<${param.name}:${paramType}>`);
|
|
444
|
+
} else {
|
|
445
|
+
parts.push(`[${param.name}:${paramType}]`);
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
return parts.join(' ');
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
/**
|
|
453
|
+
* 转换为 Tool 对象
|
|
454
|
+
*/
|
|
455
|
+
toTool(): Tool {
|
|
456
|
+
if (!this.#execute) {
|
|
457
|
+
throw new Error(`Tool "${this.#name}" has no execute() defined`);
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
const tool: Tool = {
|
|
461
|
+
name: this.#name,
|
|
462
|
+
description: this.#description,
|
|
463
|
+
parameters: this.#buildParameters(),
|
|
464
|
+
execute: this.#execute,
|
|
465
|
+
};
|
|
466
|
+
|
|
467
|
+
// 添加可选字段
|
|
468
|
+
if (this.#platforms.length > 0) tool.platforms = this.#platforms;
|
|
469
|
+
if (this.#scopes.length > 0) tool.scopes = this.#scopes;
|
|
470
|
+
if (this.#permissionLevel !== 'user') tool.permissionLevel = this.#permissionLevel;
|
|
471
|
+
if (this.#permissions.length > 0) tool.permissions = this.#permissions;
|
|
472
|
+
if (this.#tags.length > 0) tool.tags = this.#tags;
|
|
473
|
+
if (this.#hidden) tool.hidden = this.#hidden;
|
|
474
|
+
if (this.#source) tool.source = this.#source;
|
|
475
|
+
|
|
476
|
+
// 命令配置:如果没有定义 command 回调,则不生成命令
|
|
477
|
+
if (!this.#commandCallback) {
|
|
478
|
+
tool.command = false;
|
|
479
|
+
} else {
|
|
480
|
+
tool.command = {
|
|
481
|
+
...this.#commandConfig,
|
|
482
|
+
pattern: this.#generatePattern(),
|
|
483
|
+
enabled: true,
|
|
484
|
+
};
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
return tool;
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
/**
|
|
491
|
+
* 获取 action 回调(命令回调,如果有)
|
|
492
|
+
*/
|
|
493
|
+
getActionCallback(): MessageCommand.Callback<RegisteredAdapter> | undefined {
|
|
494
|
+
return this.#commandCallback;
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
/**
|
|
498
|
+
* 转换为 JSON 格式(供 AI 使用,不包含 execute 函数)
|
|
499
|
+
* 符合 OpenAI Function Calling 规范
|
|
500
|
+
*/
|
|
501
|
+
toJSON(): {
|
|
502
|
+
name: string;
|
|
503
|
+
description: string;
|
|
504
|
+
parameters: ToolParametersSchema;
|
|
505
|
+
platforms?: string[];
|
|
506
|
+
scopes?: ToolScope[];
|
|
507
|
+
permissionLevel?: ToolPermissionLevel;
|
|
508
|
+
tags?: string[];
|
|
509
|
+
} {
|
|
510
|
+
const json: ReturnType<ZhinTool['toJSON']> = {
|
|
511
|
+
name: this.#name,
|
|
512
|
+
description: this.#description,
|
|
513
|
+
parameters: this.#buildParameters(),
|
|
514
|
+
};
|
|
515
|
+
|
|
516
|
+
// 添加可选字段
|
|
517
|
+
if (this.#platforms.length > 0) json.platforms = this.#platforms;
|
|
518
|
+
if (this.#scopes.length > 0) json.scopes = this.#scopes;
|
|
519
|
+
if (this.#permissionLevel !== 'user') json.permissionLevel = this.#permissionLevel;
|
|
520
|
+
if (this.#tags.length > 0) json.tags = this.#tags;
|
|
521
|
+
|
|
522
|
+
return json;
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
/**
|
|
526
|
+
* 输出帮助信息(类似 MessageCommand)
|
|
527
|
+
*/
|
|
528
|
+
get help(): string {
|
|
529
|
+
const lines: string[] = [this.#generatePattern()];
|
|
530
|
+
if (this.#description) lines.push(` ${this.#description}`);
|
|
531
|
+
|
|
532
|
+
// 参数信息(按顺序)
|
|
533
|
+
if (this.#params.length > 0) {
|
|
534
|
+
lines.push(' 参数:');
|
|
535
|
+
for (const param of this.#params) {
|
|
536
|
+
const required = param.required ? '(必填)' : '(可选)';
|
|
537
|
+
const desc = param.schema.description || '';
|
|
538
|
+
lines.push(` ${param.name}: ${param.schema.type} ${required} ${desc}`);
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
// 权限信息
|
|
543
|
+
if (this.#permissionLevel !== 'user') {
|
|
544
|
+
lines.push(` 权限: ${this.#permissionLevel}`);
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
// 平台限制
|
|
548
|
+
if (this.#platforms.length > 0) {
|
|
549
|
+
lines.push(` 平台: ${this.#platforms.join(', ')}`);
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
// 场景限制
|
|
553
|
+
if (this.#scopes.length > 0) {
|
|
554
|
+
lines.push(` 场景: ${this.#scopes.join(', ')}`);
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
// 命令说明
|
|
558
|
+
if (this.#commandConfig.usage?.length) {
|
|
559
|
+
lines.push(' 用法:');
|
|
560
|
+
for (const u of this.#commandConfig.usage) {
|
|
561
|
+
lines.push(` ${u}`);
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
// 命令示例
|
|
566
|
+
if (this.#commandConfig.examples?.length) {
|
|
567
|
+
lines.push(' 示例:');
|
|
568
|
+
for (const e of this.#commandConfig.examples) {
|
|
569
|
+
lines.push(` ${e}`);
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
return lines.join('\n');
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
/**
|
|
577
|
+
* 输出简短信息
|
|
578
|
+
*/
|
|
579
|
+
toString(): string {
|
|
580
|
+
return `[ZhinTool: ${this.#name}] ${this.#description}`;
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
// 为了兼容 addTool,让 ZhinTool 可以隐式转换为 Tool
|
|
585
|
+
// 通过在 ToolService 中检查是否是 ZhinTool 实例
|
|
586
|
+
export function isZhinTool(obj: any): obj is ZhinTool {
|
|
587
|
+
return obj instanceof ZhinTool;
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
// ============================================================================
|
|
591
|
+
// ToolService 类型定义
|
|
592
|
+
// ============================================================================
|
|
593
|
+
|
|
594
|
+
/**
|
|
595
|
+
* 工具输入类型(支持 Tool 对象或 ZhinTool 实例)
|
|
596
|
+
*/
|
|
597
|
+
export type ToolInput = Tool | ZhinTool;
|
|
598
|
+
|
|
599
|
+
/**
|
|
600
|
+
* ToolService 扩展方法类型
|
|
601
|
+
*/
|
|
602
|
+
export interface ToolContextExtensions {
|
|
603
|
+
/** 添加工具(自动生成命令) */
|
|
604
|
+
addTool(tool: ToolInput): () => void;
|
|
605
|
+
/** 仅添加工具,不生成命令 */
|
|
606
|
+
addToolOnly(tool: ToolInput): () => void;
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
// 扩展 Plugin 接口
|
|
610
|
+
declare module "../plugin.js" {
|
|
611
|
+
namespace Plugin {
|
|
612
|
+
interface Extensions extends ToolContextExtensions {}
|
|
613
|
+
interface Contexts {
|
|
614
|
+
tool: ToolService;
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
/**
|
|
620
|
+
* 工具服务
|
|
621
|
+
*/
|
|
622
|
+
export interface ToolService {
|
|
623
|
+
/** 所有注册的工具 */
|
|
624
|
+
readonly tools: Map<string, Tool>;
|
|
625
|
+
|
|
626
|
+
/** 工具对应的命令(如果生成了) */
|
|
627
|
+
readonly toolCommands: Map<string, MessageCommand<RegisteredAdapter>>;
|
|
628
|
+
|
|
629
|
+
/** 添加工具(支持 Tool 对象或 ZhinTool 实例) */
|
|
630
|
+
add(tool: ToolInput, pluginName: string, generateCommand?: boolean): () => void;
|
|
631
|
+
|
|
632
|
+
/** 移除工具 */
|
|
633
|
+
remove(name: string): boolean;
|
|
634
|
+
|
|
635
|
+
/** 获取工具 */
|
|
636
|
+
get(name: string): Tool | undefined;
|
|
637
|
+
|
|
638
|
+
/** 获取所有工具 */
|
|
639
|
+
getAll(): Tool[];
|
|
640
|
+
|
|
641
|
+
/** 根据标签过滤工具 */
|
|
642
|
+
getByTags(tags: string[]): Tool[];
|
|
643
|
+
|
|
644
|
+
/** 执行工具 */
|
|
645
|
+
execute(name: string, args: Record<string, any>, context?: ToolContext): Promise<any>;
|
|
646
|
+
|
|
647
|
+
/** 将 Command 转换为 Tool */
|
|
648
|
+
commandToTool(command: MessageCommand<RegisteredAdapter>, pluginName: string): Tool;
|
|
649
|
+
|
|
650
|
+
/** 收集所有可用工具(包括从 Command 转换的) */
|
|
651
|
+
collectAll(plugin: Plugin): Tool[];
|
|
652
|
+
|
|
653
|
+
/**
|
|
654
|
+
* 根据上下文过滤工具
|
|
655
|
+
* 检查平台、场景、权限是否匹配
|
|
656
|
+
*/
|
|
657
|
+
filterByContext(tools: Tool[], context: ToolContext): Tool[];
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
/**
|
|
661
|
+
* 将 Tool 转换为 MessageCommand
|
|
662
|
+
*/
|
|
663
|
+
function toolToCommand(tool: Tool): MessageCommand<RegisteredAdapter> {
|
|
664
|
+
const pattern = generatePattern(tool);
|
|
665
|
+
const command = new MessageCommand<RegisteredAdapter>(pattern);
|
|
666
|
+
|
|
667
|
+
// 设置描述
|
|
668
|
+
command.desc(tool.description);
|
|
669
|
+
|
|
670
|
+
// 设置使用说明
|
|
671
|
+
if (tool.command && tool.command.usage) {
|
|
672
|
+
command.usage(...tool.command.usage);
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
// 设置示例
|
|
676
|
+
if (tool.command && tool.command.examples) {
|
|
677
|
+
command.examples(...tool.command.examples);
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
// 设置权限
|
|
681
|
+
if (tool.permissions?.length) {
|
|
682
|
+
command.permit(...tool.permissions);
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
// 设置执行回调
|
|
686
|
+
command.action(async (message: Message<AdapterMessage<RegisteredAdapter>>, result: MatchResult) => {
|
|
687
|
+
// 构建工具上下文
|
|
688
|
+
const context: ToolContext = {
|
|
689
|
+
platform: message.$adapter,
|
|
690
|
+
botId: message.$bot,
|
|
691
|
+
sceneId: message.$channel?.id || message.$sender.id,
|
|
692
|
+
senderId: message.$sender.id,
|
|
693
|
+
message,
|
|
694
|
+
};
|
|
695
|
+
|
|
696
|
+
// 从 MatchResult 提取参数
|
|
697
|
+
const args = extractArgsFromMatchResult(result, tool.parameters);
|
|
698
|
+
|
|
699
|
+
try {
|
|
700
|
+
const response = await tool.execute(args, context);
|
|
701
|
+
|
|
702
|
+
// 处理返回值
|
|
703
|
+
if (response === undefined || response === null) {
|
|
704
|
+
return undefined;
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
if (typeof response === 'string') {
|
|
708
|
+
return response;
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
// 对象类型,格式化输出
|
|
712
|
+
return formatToolResult(response);
|
|
713
|
+
} catch (error) {
|
|
714
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
715
|
+
return `❌ 执行失败: ${errorMsg}`;
|
|
716
|
+
}
|
|
717
|
+
});
|
|
718
|
+
|
|
719
|
+
return command;
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
/**
|
|
723
|
+
* 将 MessageCommand 转换为 Tool
|
|
724
|
+
*/
|
|
725
|
+
function commandToTool(
|
|
726
|
+
command: MessageCommand<RegisteredAdapter>,
|
|
727
|
+
pluginName: string
|
|
728
|
+
): Tool {
|
|
729
|
+
const { pattern, helpInfo } = command;
|
|
730
|
+
|
|
731
|
+
// 解析命令模式,提取参数
|
|
732
|
+
const parameters = parseCommandPattern(pattern);
|
|
733
|
+
|
|
734
|
+
return {
|
|
735
|
+
name: `cmd_${pattern.split(' ')[0].replace(/[^a-zA-Z0-9_]/g, '_')}`,
|
|
736
|
+
description: helpInfo.desc.join(' ') || `执行命令: ${pattern}`,
|
|
737
|
+
parameters,
|
|
738
|
+
source: `command:${pluginName}`,
|
|
739
|
+
tags: ['command', pluginName],
|
|
740
|
+
execute: async (args, context) => {
|
|
741
|
+
// 重建命令字符串
|
|
742
|
+
const cmdParts = [pattern.split(' ')[0]];
|
|
743
|
+
|
|
744
|
+
if (parameters.properties) {
|
|
745
|
+
for (const [key, schema] of Object.entries(parameters.properties)) {
|
|
746
|
+
if (args[key] !== undefined) {
|
|
747
|
+
cmdParts.push(String(args[key]));
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
const cmdString = cmdParts.join(' ');
|
|
753
|
+
|
|
754
|
+
// 如果有消息上下文,模拟命令执行
|
|
755
|
+
if (context?.message) {
|
|
756
|
+
// 创建一个临时消息副本,修改内容为命令字符串
|
|
757
|
+
const tempMessage = Object.create(context.message);
|
|
758
|
+
tempMessage.$content = cmdString;
|
|
759
|
+
|
|
760
|
+
const plugin = getPlugin();
|
|
761
|
+
const result = await command.handle(tempMessage, plugin);
|
|
762
|
+
return result;
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
return {
|
|
766
|
+
error: '此工具需要消息上下文才能执行',
|
|
767
|
+
command: cmdString
|
|
768
|
+
};
|
|
769
|
+
},
|
|
770
|
+
command: false, // 不再生成命令(已经是命令了)
|
|
771
|
+
};
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
/**
|
|
775
|
+
* 从 MatchResult 提取参数
|
|
776
|
+
*/
|
|
777
|
+
function extractArgsFromMatchResult(
|
|
778
|
+
result: MatchResult,
|
|
779
|
+
schema: ToolJsonSchema
|
|
780
|
+
): Record<string, any> {
|
|
781
|
+
const args: Record<string, any> = {};
|
|
782
|
+
|
|
783
|
+
// params 包含所有提取的参数
|
|
784
|
+
if (result.params) {
|
|
785
|
+
Object.assign(args, result.params);
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
// 类型转换
|
|
789
|
+
if (schema.properties) {
|
|
790
|
+
for (const [key, prop] of Object.entries(schema.properties)) {
|
|
791
|
+
if (args[key] !== undefined) {
|
|
792
|
+
if (prop.type === 'number') {
|
|
793
|
+
args[key] = Number(args[key]);
|
|
794
|
+
} else if (prop.type === 'boolean') {
|
|
795
|
+
args[key] = args[key] === 'true' || args[key] === true;
|
|
796
|
+
} else if (prop.type === 'array' && typeof args[key] === 'string') {
|
|
797
|
+
args[key] = args[key].split(',').map((s: string) => s.trim());
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
return args;
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
/**
|
|
807
|
+
* 解析命令模式,生成参数 Schema
|
|
808
|
+
* @example 'weather <city> [days:number]' => { properties: { city: {...}, days: {...} }, required: ['city'] }
|
|
809
|
+
*/
|
|
810
|
+
function parseCommandPattern(pattern: string): ToolParametersSchema {
|
|
811
|
+
const properties: Record<string, PropertySchema> = {};
|
|
812
|
+
const required: string[] = [];
|
|
813
|
+
|
|
814
|
+
// 匹配 <name:type> 或 [name:type] 格式
|
|
815
|
+
const paramRegex = /([<\[])(\w+)(?::(\w+))?([>\]])/g;
|
|
816
|
+
let match;
|
|
817
|
+
|
|
818
|
+
while ((match = paramRegex.exec(pattern)) !== null) {
|
|
819
|
+
const [, bracket, name, type] = match;
|
|
820
|
+
const isRequired = bracket === '<';
|
|
821
|
+
|
|
822
|
+
const schemaType = type === 'number' ? 'number' : type === 'boolean' ? 'boolean' : 'string';
|
|
823
|
+
properties[name] = {
|
|
824
|
+
type: schemaType,
|
|
825
|
+
description: `参数: ${name}`,
|
|
826
|
+
} as PropertySchema;
|
|
827
|
+
|
|
828
|
+
if (isRequired) {
|
|
829
|
+
required.push(name);
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
return {
|
|
834
|
+
type: 'object',
|
|
835
|
+
properties,
|
|
836
|
+
required: required.length > 0 ? required : undefined,
|
|
837
|
+
} as ToolParametersSchema;
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
/**
|
|
841
|
+
* 格式化工具执行结果
|
|
842
|
+
*/
|
|
843
|
+
function formatToolResult(result: any): string {
|
|
844
|
+
if (result === null || result === undefined) {
|
|
845
|
+
return '';
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
if (typeof result === 'string') {
|
|
849
|
+
return result;
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
if (result.error) {
|
|
853
|
+
return `❌ ${result.error}`;
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
// 尝试友好格式化
|
|
857
|
+
try {
|
|
858
|
+
return JSON.stringify(result, null, 2);
|
|
859
|
+
} catch {
|
|
860
|
+
return String(result);
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
/**
|
|
865
|
+
* 创建工具服务 Context
|
|
866
|
+
*/
|
|
867
|
+
export function createToolService(): Context<'tool', ToolContextExtensions> {
|
|
868
|
+
const tools = new Map<string, Tool>();
|
|
869
|
+
const toolCommands = new Map<string, MessageCommand<RegisteredAdapter>>();
|
|
870
|
+
const toolPluginMap = new Map<string, string>();
|
|
871
|
+
|
|
872
|
+
const value: ToolService = {
|
|
873
|
+
tools,
|
|
874
|
+
toolCommands,
|
|
875
|
+
|
|
876
|
+
add(toolInput, pluginName, generateCommand = true) {
|
|
877
|
+
// 保存原始 ZhinTool 引用(用于获取命令回调)
|
|
878
|
+
const zhinTool = isZhinTool(toolInput) ? toolInput : null;
|
|
879
|
+
|
|
880
|
+
// 转换为 Tool 对象
|
|
881
|
+
const tool: Tool = zhinTool ? zhinTool.toTool() : toolInput as Tool;
|
|
882
|
+
|
|
883
|
+
// 自动添加来源标识
|
|
884
|
+
const toolWithSource: Tool = {
|
|
885
|
+
...tool,
|
|
886
|
+
source: tool.source || `plugin:${pluginName}`,
|
|
887
|
+
tags: [...(tool.tags || []), 'plugin', pluginName],
|
|
888
|
+
};
|
|
889
|
+
|
|
890
|
+
tools.set(tool.name, toolWithSource);
|
|
891
|
+
toolPluginMap.set(tool.name, pluginName);
|
|
892
|
+
|
|
893
|
+
// 生成对应的命令
|
|
894
|
+
// 只有当 tool.command !== false 且 generateCommand 为 true 时才生成
|
|
895
|
+
if (generateCommand && tool.command !== false) {
|
|
896
|
+
let command: MessageCommand<RegisteredAdapter>;
|
|
897
|
+
|
|
898
|
+
// 如果是 ZhinTool 且有自定义 action 回调,使用自定义回调
|
|
899
|
+
const customCallback = zhinTool?.getActionCallback();
|
|
900
|
+
if (customCallback) {
|
|
901
|
+
// 使用自定义回调创建命令
|
|
902
|
+
command = new MessageCommand<RegisteredAdapter>(
|
|
903
|
+
tool.command && typeof tool.command === 'object' && tool.command.pattern
|
|
904
|
+
? tool.command.pattern
|
|
905
|
+
: generatePattern(toolWithSource)
|
|
906
|
+
);
|
|
907
|
+
|
|
908
|
+
command.desc(tool.description);
|
|
909
|
+
|
|
910
|
+
if (tool.command && typeof tool.command === 'object') {
|
|
911
|
+
if (tool.command.usage) command.usage(...tool.command.usage);
|
|
912
|
+
if (tool.command.examples) command.examples(...tool.command.examples);
|
|
913
|
+
}
|
|
914
|
+
|
|
915
|
+
if (tool.permissions?.length) {
|
|
916
|
+
command.permit(...tool.permissions);
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
// 使用自定义回调
|
|
920
|
+
command.action(customCallback);
|
|
921
|
+
} else {
|
|
922
|
+
// 使用默认的 toolToCommand(基于 tool.execute)
|
|
923
|
+
command = toolToCommand(toolWithSource);
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
toolCommands.set(tool.name, command);
|
|
927
|
+
|
|
928
|
+
// 注册到命令服务
|
|
929
|
+
const plugin = getPlugin();
|
|
930
|
+
const commandService = plugin.root.inject('command');
|
|
931
|
+
if (commandService) {
|
|
932
|
+
commandService.add(command, pluginName);
|
|
933
|
+
}
|
|
934
|
+
}
|
|
935
|
+
|
|
936
|
+
return () => value.remove(tool.name);
|
|
937
|
+
},
|
|
938
|
+
|
|
939
|
+
remove(name) {
|
|
940
|
+
const existed = tools.has(name);
|
|
941
|
+
tools.delete(name);
|
|
942
|
+
toolPluginMap.delete(name);
|
|
943
|
+
|
|
944
|
+
// 移除对应的命令
|
|
945
|
+
const command = toolCommands.get(name);
|
|
946
|
+
if (command) {
|
|
947
|
+
const plugin = getPlugin();
|
|
948
|
+
const commandService = plugin.root.inject('command');
|
|
949
|
+
if (commandService) {
|
|
950
|
+
commandService.remove(command);
|
|
951
|
+
}
|
|
952
|
+
toolCommands.delete(name);
|
|
953
|
+
}
|
|
954
|
+
|
|
955
|
+
return existed;
|
|
956
|
+
},
|
|
957
|
+
|
|
958
|
+
get(name) {
|
|
959
|
+
return tools.get(name);
|
|
960
|
+
},
|
|
961
|
+
|
|
962
|
+
getAll() {
|
|
963
|
+
return Array.from(tools.values());
|
|
964
|
+
},
|
|
965
|
+
|
|
966
|
+
getByTags(tags) {
|
|
967
|
+
return Array.from(tools.values()).filter(tool =>
|
|
968
|
+
tags.some(tag => tool.tags?.includes(tag))
|
|
969
|
+
);
|
|
970
|
+
},
|
|
971
|
+
|
|
972
|
+
async execute(name, args, context) {
|
|
973
|
+
const tool = tools.get(name);
|
|
974
|
+
if (!tool) {
|
|
975
|
+
throw new Error(`Tool "${name}" not found`);
|
|
976
|
+
}
|
|
977
|
+
return tool.execute(args, context);
|
|
978
|
+
},
|
|
979
|
+
|
|
980
|
+
commandToTool,
|
|
981
|
+
|
|
982
|
+
collectAll(plugin) {
|
|
983
|
+
const allTools: Tool[] = [];
|
|
984
|
+
|
|
985
|
+
// 1. 收集 ToolService 中的所有工具
|
|
986
|
+
allTools.push(...value.getAll());
|
|
987
|
+
|
|
988
|
+
// 2. 收集 Command 并转换为 Tool
|
|
989
|
+
const commandService = plugin.root.inject('command');
|
|
990
|
+
if (commandService) {
|
|
991
|
+
for (const command of commandService.items) {
|
|
992
|
+
// 跳过已经由 Tool 生成的命令
|
|
993
|
+
const isFromTool = Array.from(toolCommands.values()).includes(command);
|
|
994
|
+
if (!isFromTool) {
|
|
995
|
+
const toolFromCmd = commandToTool(command, 'command');
|
|
996
|
+
allTools.push(toolFromCmd);
|
|
997
|
+
}
|
|
998
|
+
}
|
|
999
|
+
}
|
|
1000
|
+
|
|
1001
|
+
// 3. 收集适配器提供的工具
|
|
1002
|
+
for (const [name, context] of plugin.root.contexts) {
|
|
1003
|
+
const adapterValue = context.value;
|
|
1004
|
+
if (adapterValue && typeof adapterValue === 'object' && 'getTools' in adapterValue) {
|
|
1005
|
+
const adapter = adapterValue as { getTools(): Tool[] };
|
|
1006
|
+
allTools.push(...adapter.getTools());
|
|
1007
|
+
}
|
|
1008
|
+
}
|
|
1009
|
+
|
|
1010
|
+
return allTools;
|
|
1011
|
+
},
|
|
1012
|
+
|
|
1013
|
+
filterByContext(tools, context) {
|
|
1014
|
+
return tools.filter(tool => canAccessTool(tool, context));
|
|
1015
|
+
},
|
|
1016
|
+
};
|
|
1017
|
+
|
|
1018
|
+
return {
|
|
1019
|
+
name: 'tool',
|
|
1020
|
+
description: '统一工具服务',
|
|
1021
|
+
value,
|
|
1022
|
+
extensions: {
|
|
1023
|
+
addTool(tool: ToolInput) {
|
|
1024
|
+
const plugin = getPlugin();
|
|
1025
|
+
const dispose = value.add(tool, plugin.name, true);
|
|
1026
|
+
plugin.onDispose(dispose);
|
|
1027
|
+
return dispose;
|
|
1028
|
+
},
|
|
1029
|
+
addToolOnly(tool: ToolInput) {
|
|
1030
|
+
const plugin = getPlugin();
|
|
1031
|
+
const dispose = value.add(tool, plugin.name, false);
|
|
1032
|
+
plugin.onDispose(dispose);
|
|
1033
|
+
return dispose;
|
|
1034
|
+
},
|
|
1035
|
+
},
|
|
1036
|
+
};
|
|
1037
|
+
}
|
|
1038
|
+
|
|
1039
|
+
// 导出类型和工具函数
|
|
1040
|
+
// 注意:ZhinTool, isZhinTool, ToolInput 已通过 export 关键字直接导出
|
|
1041
|
+
export {
|
|
1042
|
+
toolToCommand,
|
|
1043
|
+
commandToTool,
|
|
1044
|
+
canAccessTool,
|
|
1045
|
+
inferPermissionLevel,
|
|
1046
|
+
hasPermissionLevel,
|
|
1047
|
+
PERMISSION_LEVEL_PRIORITY,
|
|
1048
|
+
};
|
|
1049
|
+
|