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