@zhin.js/core 1.0.53 → 1.0.54
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 +12 -0
- package/lib/adapter.d.ts +13 -23
- package/lib/adapter.d.ts.map +1 -1
- package/lib/adapter.js +26 -71
- package/lib/adapter.js.map +1 -1
- package/lib/built/adapter-process.d.ts.map +1 -1
- package/lib/built/adapter-process.js +1 -0
- package/lib/built/adapter-process.js.map +1 -1
- package/lib/built/agent-preset.d.ts +40 -0
- package/lib/built/agent-preset.d.ts.map +1 -0
- package/lib/built/agent-preset.js +13 -0
- package/lib/built/agent-preset.js.map +1 -0
- package/lib/built/component.d.ts +2 -2
- package/lib/built/component.d.ts.map +1 -1
- package/lib/built/component.js +3 -2
- package/lib/built/component.js.map +1 -1
- package/lib/built/database.d.ts +2 -2
- package/lib/built/database.d.ts.map +1 -1
- package/lib/built/database.js.map +1 -1
- package/lib/built/skill.d.ts +10 -0
- package/lib/built/skill.d.ts.map +1 -1
- package/lib/built/skill.js.map +1 -1
- package/lib/index.d.ts +1 -0
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +2 -0
- package/lib/index.js.map +1 -1
- package/lib/plugin.d.ts +43 -95
- package/lib/plugin.d.ts.map +1 -1
- package/lib/plugin.js +135 -268
- package/lib/plugin.js.map +1 -1
- package/lib/types.d.ts +11 -0
- package/lib/types.d.ts.map +1 -1
- package/package.json +6 -6
- package/src/adapter.ts +37 -95
- package/src/built/adapter-process.ts +1 -0
- package/src/built/agent-preset.ts +55 -0
- package/src/built/component.ts +5 -3
- package/src/built/database.ts +2 -1
- package/src/built/skill.ts +14 -0
- package/src/index.ts +2 -0
- package/src/plugin.ts +198 -373
- package/src/types.ts +16 -0
package/src/plugin.ts
CHANGED
|
@@ -6,30 +6,20 @@
|
|
|
6
6
|
import { AsyncLocalStorage } from "async_hooks";
|
|
7
7
|
import { EventEmitter } from "events";
|
|
8
8
|
import { createRequire } from "module";
|
|
9
|
-
import type {
|
|
9
|
+
import type { Database, Definition } from "@zhin.js/database";
|
|
10
|
+
import { Schema } from "@zhin.js/schema";
|
|
11
|
+
import type { Models, RegisteredAdapters, AITool, ToolContext, PluginManifest } from "./types.js";
|
|
10
12
|
import * as fs from "fs";
|
|
11
13
|
import * as path from "path";
|
|
12
|
-
import { fileURLToPath
|
|
14
|
+
import { fileURLToPath } from "url";
|
|
13
15
|
import logger, { Logger } from "@zhin.js/logger";
|
|
14
16
|
import { compose, remove, resolveEntry } from "./utils.js";
|
|
15
|
-
import {
|
|
16
|
-
MessageMiddleware,
|
|
17
|
-
RegisteredAdapter,
|
|
18
|
-
MaybePromise,
|
|
19
|
-
ArrayItem,
|
|
20
|
-
SendOptions,
|
|
21
|
-
} from "./types.js";
|
|
22
|
-
import type { ConfigFeature } from "./built/config.js";
|
|
23
|
-
import type { PermissionFeature } from "./built/permission.js";
|
|
17
|
+
import { MessageMiddleware, RegisteredAdapter, MaybePromise, ArrayItem, SendOptions } from "./types.js";
|
|
24
18
|
import { Adapter, Adapters } from "./adapter.js";
|
|
25
|
-
import { Notice } from "./notice.js";
|
|
26
|
-
import { Message } from "./message.js";
|
|
27
|
-
import { Request } from "./request.js";
|
|
28
|
-
import { Feature, FeatureJSON } from "./feature.js";
|
|
29
19
|
import { createHash } from "crypto";
|
|
20
|
+
import { Feature } from "./feature.js";
|
|
30
21
|
const contextsKey = Symbol("contexts");
|
|
31
|
-
const
|
|
32
|
-
const loadedModules = new Map<string, Plugin>();
|
|
22
|
+
const loadedModules = new Map<string, Plugin>(); // 记录已加载的模块
|
|
33
23
|
const require = createRequire(import.meta.url);
|
|
34
24
|
|
|
35
25
|
|
|
@@ -74,19 +64,16 @@ function getCurrentFile(metaUrl = import.meta.url): string {
|
|
|
74
64
|
/**
|
|
75
65
|
* usePlugin - 获取或创建当前插件实例
|
|
76
66
|
* 类似 React Hooks 的设计,根据调用文件自动创建插件树
|
|
77
|
-
*
|
|
78
|
-
* 同一个文件多次调用 usePlugin() 返回同一个实例,
|
|
79
|
-
* 避免 Plugin.create() + usePlugin() 产生不必要的双层包装。
|
|
67
|
+
* 同一上下文中同一文件多次调用返回同一实例
|
|
80
68
|
*/
|
|
81
69
|
export function usePlugin(): Plugin {
|
|
82
70
|
const callerFile = getCurrentFile();
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
return parentPlugin;
|
|
71
|
+
// 如果当前 store 已是同一文件创建的插件,直接返回
|
|
72
|
+
const current = storage.getStore();
|
|
73
|
+
if (current && current.filePath.replace(/\?t=\d+$/, '') === callerFile.replace(/\?t=\d+$/, '')) {
|
|
74
|
+
return current;
|
|
88
75
|
}
|
|
89
|
-
|
|
76
|
+
const parentPlugin = current;
|
|
90
77
|
const newPlugin = new Plugin(callerFile, parentPlugin);
|
|
91
78
|
storage.enterWith(newPlugin);
|
|
92
79
|
return newPlugin;
|
|
@@ -146,11 +133,9 @@ export interface Plugin extends Plugin.Extensions { }
|
|
|
146
133
|
*/
|
|
147
134
|
export class Plugin extends EventEmitter<Plugin.Lifecycle> {
|
|
148
135
|
static [contextsKey] = [] as string[];
|
|
149
|
-
|
|
150
|
-
static [extensionOwnersKey] = new Map<string, string>();
|
|
151
|
-
|
|
136
|
+
|
|
152
137
|
#cachedName?: string;
|
|
153
|
-
#
|
|
138
|
+
#manifest?: PluginManifest | null;
|
|
154
139
|
adapters: (keyof Plugin.Contexts)[] = [];
|
|
155
140
|
started = false;
|
|
156
141
|
|
|
@@ -180,33 +165,32 @@ export class Plugin extends EventEmitter<Plugin.Lifecycle> {
|
|
|
180
165
|
};
|
|
181
166
|
// 插件功能
|
|
182
167
|
#middlewares: MessageMiddleware<RegisteredAdapter>[] = [this.#messageMiddleware];
|
|
168
|
+
|
|
169
|
+
// AI 工具
|
|
170
|
+
#tools: Map<string, AITool> = new Map();
|
|
183
171
|
|
|
184
|
-
//
|
|
185
|
-
#
|
|
186
|
-
|
|
172
|
+
// Feature 贡献追踪
|
|
173
|
+
#featureContributions = new Map<string, Set<string>>();
|
|
174
|
+
|
|
187
175
|
// 统一的清理函数集合
|
|
188
176
|
#disposables: Set<() => void | Promise<void>> = new Set();
|
|
189
|
-
|
|
190
|
-
// 记录当前插件向哪些 Feature 贡献了哪些 item
|
|
191
|
-
#featureContributions = new Map<string, Set<string>>();
|
|
192
|
-
|
|
177
|
+
|
|
193
178
|
get middleware(): MessageMiddleware<RegisteredAdapter> {
|
|
194
179
|
return compose<RegisteredAdapter>(this.#middlewares);
|
|
195
180
|
}
|
|
196
181
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
return this.#middlewares.filter(m => m !== this.#messageMiddleware);
|
|
182
|
+
recordFeatureContribution(featureName: string, itemName: string): void {
|
|
183
|
+
if (!this.#featureContributions.has(featureName)) {
|
|
184
|
+
this.#featureContributions.set(featureName, new Set());
|
|
185
|
+
}
|
|
186
|
+
this.#featureContributions.get(featureName)!.add(itemName);
|
|
203
187
|
}
|
|
204
188
|
/**
|
|
205
189
|
* 构造函数
|
|
206
190
|
*/
|
|
207
191
|
constructor(filePath: string = "", public parent?: Plugin) {
|
|
208
192
|
super();
|
|
209
|
-
|
|
193
|
+
|
|
210
194
|
// 增加 EventEmitter 监听器限制,避免警告
|
|
211
195
|
// 因为插件可能注册多个命令/组件/中间件,每个都会添加 dispose 监听器
|
|
212
196
|
this.setMaxListeners(50);
|
|
@@ -222,26 +206,15 @@ export class Plugin extends EventEmitter<Plugin.Lifecycle> {
|
|
|
222
206
|
// 绑定方法以支持解构使用
|
|
223
207
|
this.$bindMethods();
|
|
224
208
|
}
|
|
225
|
-
|
|
209
|
+
|
|
226
210
|
// 标记是否已绑定方法
|
|
227
211
|
#methodsBound = false;
|
|
228
212
|
|
|
229
213
|
/**
|
|
230
214
|
* 添加中间件
|
|
231
215
|
* 中间件用于处理消息流转
|
|
232
|
-
* @param middleware 中间件函数
|
|
233
216
|
*/
|
|
234
|
-
addMiddleware<T extends RegisteredAdapter>(middleware: MessageMiddleware<T
|
|
235
|
-
// Always register on root so middlewares reach the global chain
|
|
236
|
-
const target = this.root;
|
|
237
|
-
if (target !== this) {
|
|
238
|
-
const dispose = target.addMiddleware(middleware);
|
|
239
|
-
this.#disposables.add(dispose);
|
|
240
|
-
return () => {
|
|
241
|
-
dispose();
|
|
242
|
-
this.#disposables.delete(dispose);
|
|
243
|
-
};
|
|
244
|
-
}
|
|
217
|
+
addMiddleware<T extends RegisteredAdapter>(middleware: MessageMiddleware<T>, name?: string) {
|
|
245
218
|
this.#middlewares.push(middleware as MessageMiddleware<RegisteredAdapter>);
|
|
246
219
|
const dispose = () => {
|
|
247
220
|
remove(this.#middlewares, middleware);
|
|
@@ -252,28 +225,14 @@ export class Plugin extends EventEmitter<Plugin.Lifecycle> {
|
|
|
252
225
|
}
|
|
253
226
|
|
|
254
227
|
/**
|
|
255
|
-
*
|
|
256
|
-
* 工具可以被 AI
|
|
228
|
+
* 添加 AI 工具
|
|
229
|
+
* 工具可以被 AI 服务调用来执行操作
|
|
257
230
|
* @param tool 工具定义
|
|
258
|
-
* @param generateCommand 是否生成对应命令(默认 true)
|
|
259
231
|
* @returns 返回一个移除工具的函数
|
|
260
232
|
*/
|
|
261
|
-
addTool(
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
): () => void {
|
|
265
|
-
const toolService = this.root.inject('tool') as { addTool?: (tool: Tool, name: string, gen: boolean) => () => void } | undefined;
|
|
266
|
-
if (toolService && typeof toolService.addTool === 'function') {
|
|
267
|
-
const dispose = toolService.addTool(tool, this.name, generateCommand);
|
|
268
|
-
this.#disposables.add(dispose);
|
|
269
|
-
return () => {
|
|
270
|
-
dispose();
|
|
271
|
-
this.#disposables.delete(dispose);
|
|
272
|
-
};
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
// 回退到本地存储
|
|
276
|
-
const toolWithSource: Tool = {
|
|
233
|
+
addTool(tool: AITool): () => void {
|
|
234
|
+
// 自动添加插件源标识
|
|
235
|
+
const toolWithSource: AITool = {
|
|
277
236
|
...tool,
|
|
278
237
|
source: tool.source || `plugin:${this.name}`,
|
|
279
238
|
tags: [...(tool.tags || []), 'plugin', this.name],
|
|
@@ -286,29 +245,29 @@ export class Plugin extends EventEmitter<Plugin.Lifecycle> {
|
|
|
286
245
|
this.#disposables.add(dispose);
|
|
287
246
|
return dispose;
|
|
288
247
|
}
|
|
289
|
-
|
|
248
|
+
|
|
290
249
|
/**
|
|
291
250
|
* 获取当前插件注册的所有工具
|
|
292
251
|
*/
|
|
293
|
-
getTools():
|
|
252
|
+
getTools(): AITool[] {
|
|
294
253
|
return Array.from(this.#tools.values());
|
|
295
254
|
}
|
|
296
|
-
|
|
255
|
+
|
|
297
256
|
/**
|
|
298
257
|
* 获取当前插件及所有子插件注册的工具
|
|
299
258
|
*/
|
|
300
|
-
getAllTools():
|
|
301
|
-
const tools:
|
|
259
|
+
getAllTools(): AITool[] {
|
|
260
|
+
const tools: AITool[] = [...this.getTools()];
|
|
302
261
|
for (const child of this.children) {
|
|
303
262
|
tools.push(...child.getAllTools());
|
|
304
263
|
}
|
|
305
264
|
return tools;
|
|
306
265
|
}
|
|
307
|
-
|
|
266
|
+
|
|
308
267
|
/**
|
|
309
268
|
* 根据名称获取工具
|
|
310
269
|
*/
|
|
311
|
-
getTool(name: string):
|
|
270
|
+
getTool(name: string): AITool | undefined {
|
|
312
271
|
// 先在当前插件查找
|
|
313
272
|
const tool = this.#tools.get(name);
|
|
314
273
|
if (tool) return tool;
|
|
@@ -319,24 +278,18 @@ export class Plugin extends EventEmitter<Plugin.Lifecycle> {
|
|
|
319
278
|
}
|
|
320
279
|
return undefined;
|
|
321
280
|
}
|
|
322
|
-
|
|
281
|
+
|
|
323
282
|
/**
|
|
324
|
-
*
|
|
325
|
-
*
|
|
283
|
+
* 收集所有可用的工具(包括适配器提供的)
|
|
284
|
+
* 这是 AI 服务获取工具的主入口
|
|
326
285
|
*/
|
|
327
|
-
collectAllTools():
|
|
328
|
-
const
|
|
329
|
-
|
|
330
|
-
return toolService.collectAll(this.root);
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
// 回退到本地收集
|
|
334
|
-
const tools: Tool[] = [];
|
|
335
|
-
|
|
286
|
+
collectAllTools(): AITool[] {
|
|
287
|
+
const tools: AITool[] = [];
|
|
288
|
+
|
|
336
289
|
// 收集插件树中的所有工具
|
|
337
290
|
const rootPlugin = this.root;
|
|
338
291
|
tools.push(...rootPlugin.getAllTools());
|
|
339
|
-
|
|
292
|
+
|
|
340
293
|
// 收集所有适配器的工具
|
|
341
294
|
for (const [name, context] of rootPlugin.contexts) {
|
|
342
295
|
const value = context.value;
|
|
@@ -345,53 +298,74 @@ export class Plugin extends EventEmitter<Plugin.Lifecycle> {
|
|
|
345
298
|
tools.push(...adapter.getTools());
|
|
346
299
|
}
|
|
347
300
|
}
|
|
348
|
-
|
|
301
|
+
|
|
349
302
|
return tools;
|
|
350
303
|
}
|
|
351
304
|
|
|
352
305
|
/**
|
|
353
|
-
*
|
|
354
|
-
* 可通过以下方式声明:
|
|
355
|
-
* 1. 模块导出 `export const pluginName = 'my-plugin'`
|
|
356
|
-
* 2. 调用 `plugin.setName('my-plugin')`
|
|
357
|
-
*/
|
|
358
|
-
setName(name: string): void {
|
|
359
|
-
this.#explicitName = name;
|
|
360
|
-
this.#cachedName = name;
|
|
361
|
-
this.logger = logger.getLogger(name);
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
/**
|
|
365
|
-
* 插件名称(显式名称 > 路径推导)
|
|
306
|
+
* 插件名称
|
|
366
307
|
*/
|
|
367
308
|
get name(): string {
|
|
368
|
-
if (this.#explicitName) return this.#explicitName;
|
|
369
309
|
if (this.#cachedName) return this.#cachedName;
|
|
370
310
|
|
|
371
|
-
|
|
311
|
+
this.#cachedName = path
|
|
372
312
|
.relative(process.cwd(), this.filePath)
|
|
373
313
|
.replace(/\?t=\d+$/, "")
|
|
374
314
|
.replace(/\\/g, "/")
|
|
375
315
|
.replace(/\/index\.(js|ts)x?$/, "")
|
|
376
|
-
.replace(/\/(lib|src|dist)$/, "")
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
316
|
+
.replace(/\/(lib|src|dist)$/, "")
|
|
317
|
+
.replace(/.*\/node_modules\//, "")
|
|
318
|
+
.replace(/.*\//, "")
|
|
319
|
+
.replace(/\.(js|ts)x?$/, "");
|
|
320
|
+
|
|
321
|
+
return this.#cachedName;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* 插件清单(从 plugin.yml 或 package.json 延迟读取)
|
|
326
|
+
*/
|
|
327
|
+
get manifest(): PluginManifest | undefined {
|
|
328
|
+
if (this.#manifest !== undefined) return this.#manifest ?? undefined;
|
|
329
|
+
if (!this.filePath) {
|
|
330
|
+
this.#manifest = null;
|
|
331
|
+
return undefined;
|
|
332
|
+
}
|
|
333
|
+
const dir = path.dirname(this.filePath);
|
|
334
|
+
// 优先读取 plugin.yml
|
|
335
|
+
const ymlPath = path.join(dir, 'plugin.yml');
|
|
336
|
+
if (fs.existsSync(ymlPath)) {
|
|
337
|
+
try {
|
|
338
|
+
const content = fs.readFileSync(ymlPath, 'utf-8');
|
|
339
|
+
const match = content.match(/^name:\s*(.+)$/m);
|
|
340
|
+
const descMatch = content.match(/^description:\s*(.+)$/m);
|
|
341
|
+
const verMatch = content.match(/^version:\s*(.+)$/m);
|
|
342
|
+
if (match) {
|
|
343
|
+
this.#manifest = {
|
|
344
|
+
name: match[1].trim(),
|
|
345
|
+
description: descMatch?.[1]?.trim(),
|
|
346
|
+
version: verMatch?.[1]?.trim(),
|
|
347
|
+
};
|
|
348
|
+
return this.#manifest;
|
|
349
|
+
}
|
|
350
|
+
} catch { /* ignore */ }
|
|
382
351
|
}
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
352
|
+
// Fallback: package.json
|
|
353
|
+
const pkgPath = path.join(dir, 'package.json');
|
|
354
|
+
if (fs.existsSync(pkgPath)) {
|
|
355
|
+
try {
|
|
356
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
|
|
357
|
+
if (pkg.name) {
|
|
358
|
+
this.#manifest = {
|
|
359
|
+
name: pkg.name,
|
|
360
|
+
description: pkg.description,
|
|
361
|
+
version: pkg.version,
|
|
362
|
+
};
|
|
363
|
+
return this.#manifest;
|
|
364
|
+
}
|
|
365
|
+
} catch { /* ignore */ }
|
|
388
366
|
}
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
name = name.replace(/\.(js|ts)x?$/, "");
|
|
392
|
-
|
|
393
|
-
this.#cachedName = name;
|
|
394
|
-
return this.#cachedName;
|
|
367
|
+
this.#manifest = null;
|
|
368
|
+
return undefined;
|
|
395
369
|
}
|
|
396
370
|
|
|
397
371
|
/**
|
|
@@ -426,15 +400,16 @@ export class Plugin extends EventEmitter<Plugin.Lifecycle> {
|
|
|
426
400
|
if (!dispose) return;
|
|
427
401
|
const disposeFn = async (name: keyof Plugin.Contexts) => {
|
|
428
402
|
if (contexts.includes(name)) {
|
|
429
|
-
await dispose(this.inject(name) as
|
|
403
|
+
await dispose(this.inject(name) as any)
|
|
430
404
|
}
|
|
431
405
|
this.off('context.dispose', disposeFn)
|
|
432
406
|
sideEffect.finished = false;
|
|
433
407
|
}
|
|
434
408
|
this.on('context.dispose', disposeFn)
|
|
409
|
+
// 确保 dispose 时清理监听器(只注册一次)
|
|
435
410
|
const cleanupOnDispose = () => {
|
|
436
411
|
this.off('context.dispose', disposeFn)
|
|
437
|
-
dispose(this.inject(args[0] as
|
|
412
|
+
dispose(this.inject(args[0] as any) as any)
|
|
438
413
|
}
|
|
439
414
|
this.once('dispose', cleanupOnDispose)
|
|
440
415
|
}
|
|
@@ -448,7 +423,7 @@ export class Plugin extends EventEmitter<Plugin.Lifecycle> {
|
|
|
448
423
|
if (!this.#contextsIsReady(contexts)) return
|
|
449
424
|
contextReadyCallback()
|
|
450
425
|
}
|
|
451
|
-
inject<T extends keyof Plugin.Contexts>(name: T): Plugin.Contexts[T]
|
|
426
|
+
inject<T extends keyof Plugin.Contexts>(name: T): Plugin.Contexts[T]|undefined;
|
|
452
427
|
inject(name: string): unknown;
|
|
453
428
|
inject(name: string): unknown {
|
|
454
429
|
const context = this.root.contexts.get(name);
|
|
@@ -472,16 +447,26 @@ export class Plugin extends EventEmitter<Plugin.Lifecycle> {
|
|
|
472
447
|
/**
|
|
473
448
|
* 启动插件
|
|
474
449
|
*/
|
|
475
|
-
async start(t?:
|
|
450
|
+
async start(t?:number): Promise<void> {
|
|
476
451
|
if (this.started) return;
|
|
477
452
|
this.started = true; // 提前设置,防止重复启动
|
|
478
|
-
|
|
453
|
+
|
|
479
454
|
// 启动所有服务
|
|
480
455
|
for (const context of this.$contexts.values()) {
|
|
481
456
|
if (typeof context.mounted === "function") {
|
|
482
457
|
const result = await context.mounted(this);
|
|
483
|
-
//
|
|
484
|
-
if (!context.value)
|
|
458
|
+
// 仅在没有预设 value 时才用 mounted 返回值赋值
|
|
459
|
+
if (!context.value) {
|
|
460
|
+
context.value = result;
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
// 注册扩展方法到 Plugin.prototype
|
|
464
|
+
if (context.extensions) {
|
|
465
|
+
for (const [name, fn] of Object.entries(context.extensions)) {
|
|
466
|
+
if (typeof fn === 'function') {
|
|
467
|
+
Reflect.set(Plugin.prototype, name, fn);
|
|
468
|
+
}
|
|
469
|
+
}
|
|
485
470
|
}
|
|
486
471
|
this.dispatch('context.mounted', context.name)
|
|
487
472
|
}
|
|
@@ -490,160 +475,52 @@ export class Plugin extends EventEmitter<Plugin.Lifecycle> {
|
|
|
490
475
|
for (const child of this.children) {
|
|
491
476
|
await child.start(t);
|
|
492
477
|
}
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
*/
|
|
500
|
-
recordFeatureContribution(featureName: string, itemName: string): void {
|
|
501
|
-
if (!this.#featureContributions.has(featureName)) {
|
|
502
|
-
this.#featureContributions.set(featureName, new Set());
|
|
478
|
+
// 输出启动日志(使用 debug 级别,避免重复输出)
|
|
479
|
+
// 只在根插件或重要插件时使用 info 级别
|
|
480
|
+
if (!this.parent || this.name === 'setup') {
|
|
481
|
+
this.logger.info(`Plugin "${this.name}" ${t ? `reloaded in ${Date.now() - t}ms` : "started"}`);
|
|
482
|
+
} else {
|
|
483
|
+
this.logger.debug(`Plugin "${this.name}" ${t ? `reloaded in ${Date.now() - t}ms` : "started"}`);
|
|
503
484
|
}
|
|
504
|
-
this.#featureContributions.get(featureName)!.add(itemName);
|
|
505
485
|
}
|
|
506
|
-
|
|
507
486
|
/**
|
|
508
|
-
*
|
|
509
|
-
*
|
|
510
|
-
* 实际贡献记录在 usePlugin() 创建的内层子插件上
|
|
487
|
+
* 获取插件提供的功能
|
|
488
|
+
* 从各个服务中获取数据
|
|
511
489
|
*/
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
* 用于 Feature.toJSON(pluginName) 匹配
|
|
524
|
-
*/
|
|
525
|
-
#collectAllPluginNames(names: Set<string>): void {
|
|
526
|
-
names.add(this.name);
|
|
527
|
-
for (const child of this.children) {
|
|
528
|
-
child.#collectAllPluginNames(names);
|
|
529
|
-
}
|
|
490
|
+
get features(): Plugin.Features {
|
|
491
|
+
const commandService = this.inject('command');
|
|
492
|
+
const componentService = this.inject('component')
|
|
493
|
+
const cronService = this.inject('cron');
|
|
494
|
+
|
|
495
|
+
return {
|
|
496
|
+
commands: commandService ? commandService.items.map(c => c.pattern) : [],
|
|
497
|
+
components: componentService ? componentService.getAllNames() : [],
|
|
498
|
+
crons: cronService ? cronService.items.map(c => c.cronExpression) : [],
|
|
499
|
+
middlewares: this.#middlewares.map((m, i) => m.name || `middleware_${i}`),
|
|
500
|
+
};
|
|
530
501
|
}
|
|
531
502
|
|
|
532
503
|
/**
|
|
533
|
-
*
|
|
534
|
-
*
|
|
535
|
-
* 同时包含 middleware(方案 B: 本地构造)
|
|
536
|
-
*
|
|
537
|
-
* 注意:Plugin.create() 创建 "外层" 插件,usePlugin() 创建 "内层" 子插件。
|
|
538
|
-
* extension 方法 (addCommand 等) 通过 getPlugin() 记录在内层插件上。
|
|
539
|
-
* 因此需要遍历整个子树来收集 feature 贡献名称。
|
|
504
|
+
* 获取插件功能摘要(数组形式)
|
|
505
|
+
* 返回各功能类型的名称和数量
|
|
540
506
|
*/
|
|
541
|
-
getFeatures():
|
|
542
|
-
const result:
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
const
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
// 从 Feature 贡献中收集
|
|
552
|
-
for (const featureName of featureNames) {
|
|
553
|
-
const feature = this.inject(featureName as keyof Plugin.Contexts);
|
|
554
|
-
if (feature instanceof Feature) {
|
|
555
|
-
// 先用当前插件名尝试
|
|
556
|
-
let json = feature.toJSON(this.name);
|
|
557
|
-
if (json.count === 0) {
|
|
558
|
-
// 当前插件名匹配不到(可能名称不同),尝试后代插件名
|
|
559
|
-
for (const pName of pluginNames) {
|
|
560
|
-
if (pName === this.name) continue;
|
|
561
|
-
json = feature.toJSON(pName);
|
|
562
|
-
if (json.count > 0) break;
|
|
563
|
-
}
|
|
564
|
-
}
|
|
565
|
-
if (json.count > 0) {
|
|
566
|
-
result.push(json);
|
|
567
|
-
}
|
|
568
|
-
}
|
|
569
|
-
}
|
|
570
|
-
|
|
571
|
-
// middleware(方案 B: 本地构造,因为 middleware 是 Plugin 私有属性)
|
|
572
|
-
// 同样需要收集子插件树中的 middleware
|
|
573
|
-
const allMiddlewareNames: string[] = [];
|
|
574
|
-
const collectMiddlewares = (plugin: Plugin) => {
|
|
575
|
-
const mws = plugin.#middlewares
|
|
576
|
-
.filter(m => m !== plugin.#messageMiddleware)
|
|
577
|
-
.map((m, i) => m.name || `middleware_${i}`);
|
|
578
|
-
allMiddlewareNames.push(...mws);
|
|
579
|
-
for (const child of plugin.children) {
|
|
580
|
-
collectMiddlewares(child);
|
|
581
|
-
}
|
|
582
|
-
};
|
|
583
|
-
collectMiddlewares(this);
|
|
584
|
-
|
|
585
|
-
if (allMiddlewareNames.length > 0) {
|
|
586
|
-
result.push({
|
|
587
|
-
name: 'middleware',
|
|
588
|
-
icon: 'Layers',
|
|
589
|
-
desc: '中间件',
|
|
590
|
-
count: allMiddlewareNames.length,
|
|
591
|
-
items: allMiddlewareNames.map(name => ({ name })),
|
|
592
|
-
});
|
|
593
|
-
}
|
|
594
|
-
|
|
595
|
-
// 自动检测适配器和服务上下文(从 $contexts 中发现非 Feature 的贡献)
|
|
596
|
-
const adapterItems: { name: string; bots: number; online: number; tools: number }[] = [];
|
|
597
|
-
const serviceItems: { name: string; desc: string }[] = [];
|
|
598
|
-
|
|
599
|
-
const scanContexts = (plugin: Plugin) => {
|
|
600
|
-
for (const [name, context] of plugin.$contexts) {
|
|
601
|
-
const value = context.value;
|
|
602
|
-
if (value instanceof Adapter) {
|
|
603
|
-
adapterItems.push({
|
|
604
|
-
name,
|
|
605
|
-
bots: value.bots.size,
|
|
606
|
-
online: Array.from(value.bots.values()).filter(b => b.$connected).length,
|
|
607
|
-
tools: value.tools.size,
|
|
608
|
-
});
|
|
609
|
-
} else if (value !== undefined && !(value instanceof Feature)) {
|
|
610
|
-
// 非 Feature、非 Adapter 的上下文 = 服务
|
|
611
|
-
serviceItems.push({ name, desc: context.description || name });
|
|
612
|
-
}
|
|
613
|
-
}
|
|
614
|
-
for (const child of plugin.children) {
|
|
615
|
-
scanContexts(child);
|
|
616
|
-
}
|
|
617
|
-
};
|
|
618
|
-
scanContexts(this);
|
|
619
|
-
|
|
620
|
-
if (adapterItems.length > 0) {
|
|
621
|
-
result.push({
|
|
622
|
-
name: 'adapter',
|
|
623
|
-
icon: 'Plug',
|
|
624
|
-
desc: '适配器',
|
|
625
|
-
count: adapterItems.length,
|
|
626
|
-
items: adapterItems,
|
|
627
|
-
});
|
|
628
|
-
}
|
|
629
|
-
|
|
630
|
-
if (serviceItems.length > 0) {
|
|
631
|
-
result.push({
|
|
632
|
-
name: 'service',
|
|
633
|
-
icon: 'Server',
|
|
634
|
-
desc: '服务',
|
|
635
|
-
count: serviceItems.length,
|
|
636
|
-
items: serviceItems,
|
|
637
|
-
});
|
|
638
|
-
}
|
|
639
|
-
|
|
507
|
+
getFeatures(): Array<{ name: string; count: number }> {
|
|
508
|
+
const result: Array<{ name: string; count: number }> = [];
|
|
509
|
+
const f = this.features;
|
|
510
|
+
if (f.commands.length > 0) result.push({ name: 'command', count: f.commands.length });
|
|
511
|
+
if (f.components.length > 0) result.push({ name: 'component', count: f.components.length });
|
|
512
|
+
if (f.crons.length > 0) result.push({ name: 'cron', count: f.crons.length });
|
|
513
|
+
// #middlewares includes the default command middleware, only count user-added ones
|
|
514
|
+
const userMiddlewareCount = this.#middlewares.length - 1; // subtract default #messageMiddleware
|
|
515
|
+
if (userMiddlewareCount > 0) result.push({ name: 'middleware', count: userMiddlewareCount });
|
|
516
|
+
if (this.#tools.size > 0) result.push({ name: 'tool', count: this.#tools.size });
|
|
640
517
|
return result;
|
|
641
518
|
}
|
|
642
519
|
|
|
643
520
|
info(): Record<string, any> {
|
|
644
521
|
return {
|
|
645
522
|
[this.name]: {
|
|
646
|
-
features: this.
|
|
523
|
+
features: this.features,
|
|
647
524
|
children: this.children.map(child => child.info())
|
|
648
525
|
}
|
|
649
526
|
}
|
|
@@ -663,15 +540,13 @@ export class Plugin extends EventEmitter<Plugin.Lifecycle> {
|
|
|
663
540
|
}
|
|
664
541
|
this.children = [];
|
|
665
542
|
|
|
666
|
-
// 停止服务
|
|
543
|
+
// 停止服务
|
|
667
544
|
for (const [name, context] of this.$contexts) {
|
|
668
545
|
remove(Plugin[contextsKey], name);
|
|
546
|
+
// 移除扩展方法
|
|
669
547
|
if (context.extensions) {
|
|
670
548
|
for (const key of Object.keys(context.extensions)) {
|
|
671
|
-
|
|
672
|
-
delete (Plugin.prototype as unknown as Record<string, unknown>)[key];
|
|
673
|
-
Plugin[extensionOwnersKey].delete(key);
|
|
674
|
-
}
|
|
549
|
+
delete (Plugin.prototype as any)[key];
|
|
675
550
|
}
|
|
676
551
|
}
|
|
677
552
|
if (typeof context.dispose === "function") {
|
|
@@ -681,12 +556,12 @@ export class Plugin extends EventEmitter<Plugin.Lifecycle> {
|
|
|
681
556
|
// 清理 contexts Map
|
|
682
557
|
this.$contexts.clear();
|
|
683
558
|
|
|
684
|
-
|
|
559
|
+
// 清空缓存的名称
|
|
685
560
|
this.#cachedName = undefined;
|
|
686
561
|
|
|
687
562
|
// 触发 dispose 事件
|
|
688
563
|
this.emit("dispose");
|
|
689
|
-
|
|
564
|
+
|
|
690
565
|
// 执行所有清理函数
|
|
691
566
|
for (const dispose of this.#disposables) {
|
|
692
567
|
try {
|
|
@@ -696,17 +571,17 @@ export class Plugin extends EventEmitter<Plugin.Lifecycle> {
|
|
|
696
571
|
}
|
|
697
572
|
}
|
|
698
573
|
this.#disposables.clear();
|
|
699
|
-
|
|
574
|
+
|
|
700
575
|
// 清理 middlewares 数组(保留默认的消息中间件)
|
|
701
576
|
this.#middlewares.length = 1;
|
|
702
577
|
|
|
703
578
|
// 清理 feature 贡献记录
|
|
704
579
|
this.#featureContributions.clear();
|
|
705
|
-
|
|
580
|
+
|
|
706
581
|
if (this.parent) {
|
|
707
582
|
remove(this.parent?.children, this);
|
|
708
583
|
}
|
|
709
|
-
|
|
584
|
+
|
|
710
585
|
// 从全局 loadedModules Map 中移除,防止内存泄漏
|
|
711
586
|
if (this.filePath) {
|
|
712
587
|
try {
|
|
@@ -716,7 +591,7 @@ export class Plugin extends EventEmitter<Plugin.Lifecycle> {
|
|
|
716
591
|
// 文件可能已不存在,忽略错误
|
|
717
592
|
}
|
|
718
593
|
}
|
|
719
|
-
|
|
594
|
+
|
|
720
595
|
this.removeAllListeners();
|
|
721
596
|
this.logger.debug(`Plugin "${this.name}" stopped`);
|
|
722
597
|
}
|
|
@@ -775,19 +650,21 @@ export class Plugin extends EventEmitter<Plugin.Lifecycle> {
|
|
|
775
650
|
// ============================================================================
|
|
776
651
|
|
|
777
652
|
/**
|
|
778
|
-
*
|
|
653
|
+
* 注册上下文
|
|
779
654
|
*/
|
|
780
|
-
provide<T extends keyof Plugin.Contexts>(
|
|
655
|
+
provide<T extends keyof Plugin.Contexts>(context: Context<T>): this;
|
|
656
|
+
provide(feature: Feature<unknown>): this;
|
|
657
|
+
provide(target: Context<keyof Plugin.Contexts> | Feature<unknown>): this {
|
|
781
658
|
if (target instanceof Feature) {
|
|
782
659
|
const feature = target;
|
|
783
|
-
const
|
|
784
|
-
name: feature.name as
|
|
660
|
+
const ctx: Context<keyof Plugin.Contexts> = {
|
|
661
|
+
name: feature.name as keyof Plugin.Contexts,
|
|
785
662
|
description: feature.desc,
|
|
786
|
-
value: feature as Plugin.Contexts[
|
|
663
|
+
value: feature as Plugin.Contexts[keyof Plugin.Contexts],
|
|
787
664
|
mounted: feature.mounted
|
|
788
665
|
? async (plugin: Plugin) => {
|
|
789
666
|
await feature.mounted!(plugin);
|
|
790
|
-
return feature as Plugin.Contexts[
|
|
667
|
+
return feature as Plugin.Contexts[keyof Plugin.Contexts];
|
|
791
668
|
}
|
|
792
669
|
: undefined,
|
|
793
670
|
dispose: feature.dispose
|
|
@@ -795,31 +672,22 @@ export class Plugin extends EventEmitter<Plugin.Lifecycle> {
|
|
|
795
672
|
: undefined,
|
|
796
673
|
extensions: feature.extensions,
|
|
797
674
|
};
|
|
798
|
-
return this.provide(
|
|
675
|
+
return this.provide(ctx);
|
|
799
676
|
}
|
|
800
|
-
|
|
801
677
|
const context = target;
|
|
802
678
|
if (!Plugin[contextsKey].includes(context.name as string)) {
|
|
803
679
|
Plugin[contextsKey].push(context.name as string);
|
|
804
680
|
}
|
|
805
681
|
this.logger.debug(`Context "${context.name as string}" provided`);
|
|
806
|
-
|
|
807
|
-
//
|
|
808
|
-
// On collision, log a warning but allow override (last-write-wins).
|
|
682
|
+
this.$contexts.set(context.name as string, context);
|
|
683
|
+
// 立即注册扩展方法到 Plugin.prototype,确保后续 import 的插件可用
|
|
809
684
|
if (context.extensions) {
|
|
810
|
-
const extNames: string[] = [];
|
|
811
685
|
for (const [name, fn] of Object.entries(context.extensions)) {
|
|
812
686
|
if (typeof fn === 'function') {
|
|
813
|
-
if (Reflect.has(Plugin.prototype, name) && !Plugin[extensionOwnersKey].has(name)) {
|
|
814
|
-
this.logger.warn(`Extension method "${name}" shadows an existing Plugin method`);
|
|
815
|
-
}
|
|
816
687
|
Reflect.set(Plugin.prototype, name, fn);
|
|
817
|
-
Plugin[extensionOwnersKey].set(name, context.name as string);
|
|
818
|
-
extNames.push(name);
|
|
819
688
|
}
|
|
820
689
|
}
|
|
821
690
|
}
|
|
822
|
-
this.$contexts.set(context.name as string, context);
|
|
823
691
|
return this;
|
|
824
692
|
}
|
|
825
693
|
|
|
@@ -830,7 +698,7 @@ export class Plugin extends EventEmitter<Plugin.Lifecycle> {
|
|
|
830
698
|
/**
|
|
831
699
|
* 导入插件
|
|
832
700
|
*/
|
|
833
|
-
async import(entry: string,
|
|
701
|
+
async import(entry: string,t?:number): Promise<Plugin> {
|
|
834
702
|
if (!entry) throw new Error(`Plugin entry not found: ${entry}`);
|
|
835
703
|
const resolved = resolveEntry(path.isAbsolute(entry) ?
|
|
836
704
|
entry :
|
|
@@ -844,7 +712,7 @@ export class Plugin extends EventEmitter<Plugin.Lifecycle> {
|
|
|
844
712
|
|
|
845
713
|
// 避免重复加载同一路径的插件
|
|
846
714
|
const normalized = realPath.replace(/\?t=\d+$/, '').replace(/\\/g, '/');
|
|
847
|
-
const existing = this.children.find(child =>
|
|
715
|
+
const existing = this.children.find(child =>
|
|
848
716
|
child.filePath.replace(/\?t=\d+$/, '').replace(/\\/g, '/') === normalized
|
|
849
717
|
);
|
|
850
718
|
if (existing) {
|
|
@@ -867,7 +735,7 @@ export class Plugin extends EventEmitter<Plugin.Lifecycle> {
|
|
|
867
735
|
*/
|
|
868
736
|
async reload(plugin: Plugin = this): Promise<void> {
|
|
869
737
|
this.logger.info(`Plugin "${plugin.name}" reloading...`);
|
|
870
|
-
const now
|
|
738
|
+
const now=Date.now();
|
|
871
739
|
if (!plugin.parent) {
|
|
872
740
|
// 根插件重载 = 退出进程(由 CLI 重启)
|
|
873
741
|
return process.exit(51);
|
|
@@ -914,8 +782,7 @@ export class Plugin extends EventEmitter<Plugin.Lifecycle> {
|
|
|
914
782
|
static #coreMethods = new Set([
|
|
915
783
|
'addMiddleware', 'useContext', 'inject', 'contextIsReady',
|
|
916
784
|
'start', 'stop', 'onMounted', 'onDispose',
|
|
917
|
-
'dispatch', 'broadcast', 'provide', 'import', 'reload', 'watch', 'info'
|
|
918
|
-
'recordFeatureContribution', 'getFeatures',
|
|
785
|
+
'dispatch', 'broadcast', 'provide', 'import', 'reload', 'watch', 'info'
|
|
919
786
|
]);
|
|
920
787
|
|
|
921
788
|
/**
|
|
@@ -924,12 +791,12 @@ export class Plugin extends EventEmitter<Plugin.Lifecycle> {
|
|
|
924
791
|
$bindMethods(): void {
|
|
925
792
|
if (this.#methodsBound) return;
|
|
926
793
|
this.#methodsBound = true;
|
|
927
|
-
|
|
794
|
+
|
|
928
795
|
const proto = Object.getPrototypeOf(this);
|
|
929
796
|
for (const key of Plugin.#coreMethods) {
|
|
930
797
|
const value = proto[key];
|
|
931
798
|
if (typeof value === "function") {
|
|
932
|
-
(this as
|
|
799
|
+
(this as any)[key] = value.bind(this);
|
|
933
800
|
}
|
|
934
801
|
}
|
|
935
802
|
}
|
|
@@ -957,53 +824,20 @@ export class Plugin extends EventEmitter<Plugin.Lifecycle> {
|
|
|
957
824
|
|
|
958
825
|
const plugin = new Plugin(realPath, parent);
|
|
959
826
|
plugin.fileHash = getFileHash(entryFile);
|
|
960
|
-
|
|
827
|
+
|
|
961
828
|
// 先记录,防止循环依赖时重复加载
|
|
962
829
|
loadedModules.set(realPath, plugin);
|
|
963
830
|
|
|
964
|
-
let mod: any;
|
|
965
831
|
await storage.run(plugin, async () => {
|
|
966
|
-
|
|
832
|
+
await import(`${import.meta.resolve(entryFile)}?t=${Date.now()}`);
|
|
967
833
|
});
|
|
968
834
|
|
|
969
|
-
// 支持模块显式声明插件名称:export const pluginName = 'my-plugin'
|
|
970
|
-
if (mod?.pluginName && typeof mod.pluginName === 'string') {
|
|
971
|
-
plugin.setName(mod.pluginName);
|
|
972
|
-
}
|
|
973
|
-
|
|
974
835
|
return plugin;
|
|
975
836
|
}
|
|
976
837
|
}
|
|
977
838
|
export function defineContext<T extends keyof Plugin.Contexts, E extends Partial<Plugin.Extensions> = {}>(options: Context<T, E>): Context<T, E> {
|
|
978
839
|
return options;
|
|
979
840
|
}
|
|
980
|
-
|
|
981
|
-
/**
|
|
982
|
-
* 声明式定义插件。
|
|
983
|
-
* 提供显式名称 + setup 函数,自动设置当前插件名称并执行 setup。
|
|
984
|
-
*
|
|
985
|
-
* @example
|
|
986
|
-
* ```typescript
|
|
987
|
-
* // plugins/weather/index.ts
|
|
988
|
-
* export default definePlugin({
|
|
989
|
-
* name: 'weather',
|
|
990
|
-
* setup(plugin) {
|
|
991
|
-
* plugin.addTool({ ... });
|
|
992
|
-
* },
|
|
993
|
-
* });
|
|
994
|
-
* ```
|
|
995
|
-
*/
|
|
996
|
-
export function definePlugin(options: {
|
|
997
|
-
name: string;
|
|
998
|
-
setup: (plugin: Plugin) => MaybePromise<void>;
|
|
999
|
-
}): { pluginName: string } {
|
|
1000
|
-
const plugin = usePlugin();
|
|
1001
|
-
if (options.name) {
|
|
1002
|
-
plugin.setName(options.name);
|
|
1003
|
-
}
|
|
1004
|
-
options.setup(plugin);
|
|
1005
|
-
return { pluginName: options.name };
|
|
1006
|
-
}
|
|
1007
841
|
export interface Context<T extends keyof Plugin.Contexts = keyof Plugin.Contexts, E extends Partial<Plugin.Extensions> = {}> {
|
|
1008
842
|
name: T;
|
|
1009
843
|
description: string
|
|
@@ -1016,18 +850,17 @@ export interface Context<T extends keyof Plugin.Contexts = keyof Plugin.Contexts
|
|
|
1016
850
|
// ============================================================================
|
|
1017
851
|
// 类型定义
|
|
1018
852
|
// ============================================================================
|
|
1019
|
-
|
|
1020
|
-
/** 登录辅助:bot.login.pending 事件 payload */
|
|
1021
|
-
export interface BotLoginPendingTask {
|
|
1022
|
-
id: string;
|
|
1023
|
-
adapter: string;
|
|
1024
|
-
botId: string;
|
|
1025
|
-
type: string;
|
|
1026
|
-
payload?: { message?: string; image?: string; url?: string; [key: string]: unknown };
|
|
1027
|
-
createdAt: number;
|
|
1028
|
-
}
|
|
1029
|
-
|
|
1030
853
|
export namespace Plugin {
|
|
854
|
+
/**
|
|
855
|
+
* 插件提供的功能
|
|
856
|
+
*/
|
|
857
|
+
export interface Features {
|
|
858
|
+
commands: string[];
|
|
859
|
+
components: string[];
|
|
860
|
+
crons: string[];
|
|
861
|
+
middlewares: string[];
|
|
862
|
+
}
|
|
863
|
+
|
|
1031
864
|
/**
|
|
1032
865
|
* 生命周期事件
|
|
1033
866
|
*/
|
|
@@ -1042,30 +875,22 @@ export namespace Plugin {
|
|
|
1042
875
|
'before.sendMessage': [SendOptions];
|
|
1043
876
|
"context.mounted": [keyof Plugin.Contexts];
|
|
1044
877
|
"context.dispose": [keyof Plugin.Contexts];
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
'message.receive': [Message];
|
|
1050
|
-
[key: `notice.${string}`]: [Notice];
|
|
1051
|
-
// Request 事件
|
|
1052
|
-
'request.receive': [Request];
|
|
1053
|
-
[key: `request.${string}`]: [Request];
|
|
878
|
+
"message.receive": [any];
|
|
879
|
+
"bot.login.pending": [import('./built/login-assist.js').PendingLoginTask];
|
|
880
|
+
"request.receive": [any];
|
|
881
|
+
"notice.receive": [any];
|
|
1054
882
|
}
|
|
1055
883
|
|
|
1056
884
|
/**
|
|
1057
885
|
* 服务类型扩展点
|
|
1058
886
|
* 各个 Context 通过 declare module 扩展此接口
|
|
1059
887
|
*/
|
|
1060
|
-
export interface Contexts extends Adapters {
|
|
1061
|
-
|
|
1062
|
-
permission: PermissionFeature;
|
|
1063
|
-
}
|
|
1064
|
-
|
|
888
|
+
export interface Contexts extends Adapters {}
|
|
889
|
+
|
|
1065
890
|
/**
|
|
1066
891
|
* Service 扩展方法类型
|
|
1067
892
|
* 这些方法由各个 Context 的 extensions 提供
|
|
1068
893
|
* 在 Plugin.start() 时自动注册到 Plugin.prototype
|
|
1069
894
|
*/
|
|
1070
|
-
export interface Extensions {
|
|
895
|
+
export interface Extensions {}
|
|
1071
896
|
}
|