foliko 1.0.87 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agent/data/default.json +3 -108
- package/.agent/data/plugins-state.json +34 -1
- package/.agent/mcp_config.json +0 -1
- package/.agent/memory/core.md +1 -0
- package/.agent/memory/project/mnn93ogy-ypjn27.md +9 -0
- package/.agent/memory/project/mnn98fqy-5nhc1u.md +25 -0
- package/.agent/memory/user/mnm67t9m-x8rekk.md +9 -0
- package/.agent/memory/user/mnn5mmqh-w6aktx.md +11 -0
- package/.agent/memory/user/mnnbfhhn-dk1bd1.md +22 -0
- package/.agent/plugins/__pycache__/file_writer.cpython-312.pyc +0 -0
- package/.agent/plugins/poster-plugin/README.md +304 -0
- package/.agent/plugins/poster-plugin/fonts/PatuaOne-Regular.ttf +0 -0
- package/.agent/plugins/poster-plugin/fonts//345/276/256/350/275/257/351/233/205/351/273/221.ttf +0 -0
- package/.agent/plugins/poster-plugin/fonts//345/276/256/350/275/257/351/233/205/351/273/221/347/262/227/344/275/223.ttf +0 -0
- package/.agent/plugins/poster-plugin/index.js +13 -0
- package/.agent/plugins/poster-plugin/package.json +28 -0
- package/.agent/plugins/poster-plugin/src/canvas.js +161 -0
- package/.agent/plugins/poster-plugin/src/components/arrow.js +84 -0
- package/.agent/plugins/poster-plugin/src/components/avatar.js +71 -0
- package/.agent/plugins/poster-plugin/src/components/badge.js +85 -0
- package/.agent/plugins/poster-plugin/src/components/card.js +88 -0
- package/.agent/plugins/poster-plugin/src/components/chart.js +127 -0
- package/.agent/plugins/poster-plugin/src/components/chip.js +88 -0
- package/.agent/plugins/poster-plugin/src/components/columns.js +107 -0
- package/.agent/plugins/poster-plugin/src/components/cta.js +85 -0
- package/.agent/plugins/poster-plugin/src/components/divider.js +55 -0
- package/.agent/plugins/poster-plugin/src/components/feature.js +85 -0
- package/.agent/plugins/poster-plugin/src/components/featureGrid.js +112 -0
- package/.agent/plugins/poster-plugin/src/components/grid.js +118 -0
- package/.agent/plugins/poster-plugin/src/components/imageFrame.js +155 -0
- package/.agent/plugins/poster-plugin/src/components/index.js +62 -0
- package/.agent/plugins/poster-plugin/src/components/listItem.js +146 -0
- package/.agent/plugins/poster-plugin/src/components/notification.js +123 -0
- package/.agent/plugins/poster-plugin/src/components/progress.js +79 -0
- package/.agent/plugins/poster-plugin/src/components/progressCircle.js +117 -0
- package/.agent/plugins/poster-plugin/src/components/quote.js +97 -0
- package/.agent/plugins/poster-plugin/src/components/rating.js +85 -0
- package/.agent/plugins/poster-plugin/src/components/star.js +70 -0
- package/.agent/plugins/poster-plugin/src/components/statCard.js +105 -0
- package/.agent/plugins/poster-plugin/src/components/stepper.js +118 -0
- package/.agent/plugins/poster-plugin/src/components/table.js +159 -0
- package/.agent/plugins/poster-plugin/src/components/tagCloud.js +78 -0
- package/.agent/plugins/poster-plugin/src/components/timeline.js +105 -0
- package/.agent/plugins/poster-plugin/src/components/watermark.js +52 -0
- package/.agent/plugins/poster-plugin/src/composer.js +1904 -0
- package/.agent/plugins/poster-plugin/src/elements/artText.js +60 -0
- package/.agent/plugins/poster-plugin/src/elements/background.js +52 -0
- package/.agent/plugins/poster-plugin/src/elements/circle.js +31 -0
- package/.agent/plugins/poster-plugin/src/elements/image.js +71 -0
- package/.agent/plugins/poster-plugin/src/elements/index.js +26 -0
- package/.agent/plugins/poster-plugin/src/elements/line.js +23 -0
- package/.agent/plugins/poster-plugin/src/elements/polygon.js +32 -0
- package/.agent/plugins/poster-plugin/src/elements/rectangle.js +32 -0
- package/.agent/plugins/poster-plugin/src/elements/svg.js +92 -0
- package/.agent/plugins/poster-plugin/src/elements/text.js +38 -0
- package/.agent/plugins/poster-plugin/src/fonts.js +118 -0
- package/.agent/plugins/poster-plugin/src/index.js +1659 -0
- package/.agent/plugins/poster-plugin/src/presets.js +36 -0
- package/.agent/plugins/poster-plugin/src/templates/business.js +60 -0
- package/.agent/plugins/poster-plugin/src/templates/gradient.js +64 -0
- package/.agent/plugins/poster-plugin/src/templates/index.js +43 -0
- package/.agent/plugins/poster-plugin/src/templates/modern.js +69 -0
- package/.agent/plugins/poster-plugin/src/templates/simple.js +58 -0
- package/.agent/plugins/poster-plugin/src/templates/social.js +62 -0
- package/.agent/plugins/poster-plugin/src/templates/tech.js +84 -0
- package/.agent/sessions/cli_default.json +24265 -0
- package/.agent/weixin.json +6 -0
- package/.claude/settings.local.json +5 -8
- package/CLAUDE.md +144 -108
- package/docs/CONTEXT_DESIGN.md +1596 -0
- package/examples/test-concurrent-chat.js +60 -60
- package/output/beef-love-poster.png +0 -0
- package/package.json +2 -2
- package/plugins/default-plugins.js +2 -1
- package/plugins/extension-executor-plugin.js +11 -0
- package/plugins/memory-plugin.js +984 -0
- package/plugins/session-plugin.js +57 -1
- package/plugins/weixin-plugin.js +24 -22
- package/skills/poster-guide/SKILL.md +743 -0
- package/skills/python-plugin-dev/SKILL.md +238 -238
- package/skills/skill-guide/SKILL.md +130 -108
- package/src/capabilities/skill-manager.js +99 -0
- package/src/core/agent-chat.js +538 -138
- package/src/core/agent-context.js +188 -0
- package/src/core/agent.js +6 -2
- package/src/core/context-manager.js +283 -0
- package/src/core/framework.js +264 -3
- package/src/core/plugin-manager.js +79 -2
- package/src/core/request-context.js +98 -0
- package/src/core/session-context.js +341 -0
- package/src/core/session-storage.js +274 -0
- package/src/executors/mcp-executor.js +2 -2
- package/src/utils/index.js +239 -67
- package/src/utils/plugin-helpers.js +17 -0
- package//346/265/267/346/212/245/346/217/222/344/273/266.md +621 -0
- package/.agent/plugins/__pycache__/test_plugin.cpython-312.pyc +0 -0
- package/.agent/plugins/temp-repo/LICENSE +0 -201
- package/.agent/plugins/temp-repo/puppeteer-plugin/README.md +0 -147
- package/.agent/plugins/temp-repo/puppeteer-plugin/index.js +0 -1418
- package/.agent/plugins/temp-repo/puppeteer-plugin/package.json +0 -9
- package/.agent/plugins/test_plugin.py +0 -304
- package/examples/test-chat-debug.js +0 -102
- package/examples/test-chat-result.js +0 -76
- package/examples/test-chat-stream-diff.js +0 -63
package/src/core/framework.js
CHANGED
|
@@ -10,6 +10,9 @@ const { PluginManager } = require('./plugin-manager');
|
|
|
10
10
|
const { ToolRegistry } = require('./tool-registry');
|
|
11
11
|
const { Agent } = require('./agent');
|
|
12
12
|
const { SubAgentConfigManager } = require('./sub-agent-config');
|
|
13
|
+
const { ContextManager } = require('./context-manager');
|
|
14
|
+
const { SessionContext } = require('./session-context');
|
|
15
|
+
const { SessionStorageAdapter, getDefaultAdapter } = require('./session-storage');
|
|
13
16
|
|
|
14
17
|
// 添加框架目录的 node_modules 到全局搜索路径
|
|
15
18
|
// 让项目插件能 require 框架内置的包(如 zod)
|
|
@@ -18,9 +21,14 @@ if (!module.paths.includes(frameworkNodeModules)) {
|
|
|
18
21
|
module.paths.unshift(frameworkNodeModules);
|
|
19
22
|
}
|
|
20
23
|
|
|
21
|
-
// AsyncLocalStorage
|
|
24
|
+
// 现有的 AsyncLocalStorage(保持向后兼容)
|
|
22
25
|
const asyncLocalStorage = new AsyncLocalStorage();
|
|
23
26
|
|
|
27
|
+
// 新增:三层 AsyncLocalStorage
|
|
28
|
+
const requestStorage = new AsyncLocalStorage(); // Request 级别
|
|
29
|
+
const sessionStorage = new AsyncLocalStorage(); // Session 级别
|
|
30
|
+
const agentStorage = new AsyncLocalStorage(); // Agent 级别
|
|
31
|
+
|
|
24
32
|
class Framework extends EventEmitter {
|
|
25
33
|
/**
|
|
26
34
|
* @param {Object} config - 配置
|
|
@@ -48,6 +56,20 @@ class Framework extends EventEmitter {
|
|
|
48
56
|
// 执行上下文(工具调用时可用)
|
|
49
57
|
this._executionContext = null;
|
|
50
58
|
|
|
59
|
+
// ========== 分层上下文系统 ==========
|
|
60
|
+
// Context 管理器
|
|
61
|
+
this._contextManager = new ContextManager(this);
|
|
62
|
+
|
|
63
|
+
// SessionContext 缓存(轻量管理,不依赖 ContextManager)
|
|
64
|
+
this._sessionContexts = new Map(); // sessionId → SessionContext
|
|
65
|
+
|
|
66
|
+
// Session 存储适配器(默认使用文件存储)
|
|
67
|
+
this._sessionStorage = null;
|
|
68
|
+
this._sessionStorageConfig = config.sessionStorage || {
|
|
69
|
+
type: 'file',
|
|
70
|
+
baseDir: '.agent/sessions',
|
|
71
|
+
};
|
|
72
|
+
|
|
51
73
|
// 事件转发 - ToolRegistry 事件
|
|
52
74
|
this.toolRegistry.on('tool:registered', (tool) => {
|
|
53
75
|
this.emit('tool:registered', tool);
|
|
@@ -283,6 +305,239 @@ class Framework extends EventEmitter {
|
|
|
283
305
|
*/
|
|
284
306
|
clearExecutionContext() {}
|
|
285
307
|
|
|
308
|
+
// ============================================================================
|
|
309
|
+
// 分层上下文系统 - Session Context
|
|
310
|
+
// ============================================================================
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* 获取或创建 SessionContext
|
|
314
|
+
* @param {string} sessionId - 会话 ID
|
|
315
|
+
* @param {Object} options - 配置选项
|
|
316
|
+
* @returns {SessionContext}
|
|
317
|
+
*/
|
|
318
|
+
getOrCreateSessionContext(sessionId, options = {}) {
|
|
319
|
+
if (!sessionId) {
|
|
320
|
+
return null;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// 先检查内存缓存
|
|
324
|
+
let ctx = this._sessionContexts.get(sessionId);
|
|
325
|
+
if (ctx) {
|
|
326
|
+
return ctx;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// 尝试从存储同步加载(仅文件存储支持)
|
|
330
|
+
const storage = this._getSessionStorage();
|
|
331
|
+
const loadedCtx = SessionContext.loadSync(sessionId, this, { ...options, storage });
|
|
332
|
+
if (loadedCtx) {
|
|
333
|
+
this._sessionContexts.set(sessionId, loadedCtx);
|
|
334
|
+
this.emit('session:context-created', loadedCtx);
|
|
335
|
+
return loadedCtx;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
// 创建新的 SessionContext
|
|
339
|
+
ctx = new SessionContext(sessionId, this, { ...options, storage });
|
|
340
|
+
this._sessionContexts.set(sessionId, ctx);
|
|
341
|
+
this.emit('session:context-created', ctx);
|
|
342
|
+
return ctx;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
/**
|
|
346
|
+
* 异步加载 SessionContext(如果存在)
|
|
347
|
+
* @param {string} sessionId
|
|
348
|
+
* @param {Object} options
|
|
349
|
+
* @returns {Promise<SessionContext|null>}
|
|
350
|
+
*/
|
|
351
|
+
async loadSessionContext(sessionId, options = {}) {
|
|
352
|
+
if (!sessionId) {
|
|
353
|
+
return null;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
// 先检查内存缓存
|
|
357
|
+
let ctx = this._sessionContexts.get(sessionId);
|
|
358
|
+
if (ctx) {
|
|
359
|
+
return ctx;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
const storage = this._getSessionStorage();
|
|
363
|
+
ctx = await SessionContext.load(sessionId, this, { ...options, storage });
|
|
364
|
+
if (ctx) {
|
|
365
|
+
this._sessionContexts.set(sessionId, ctx);
|
|
366
|
+
this.emit('session:context-created', ctx);
|
|
367
|
+
}
|
|
368
|
+
return ctx;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
/**
|
|
372
|
+
* 获取 Session 存储适配器
|
|
373
|
+
* @returns {SessionStorageAdapter}
|
|
374
|
+
* @private
|
|
375
|
+
*/
|
|
376
|
+
_getSessionStorage() {
|
|
377
|
+
if (!this._sessionStorage) {
|
|
378
|
+
this._sessionStorage = new SessionStorageAdapter(this._sessionStorageConfig);
|
|
379
|
+
}
|
|
380
|
+
return this._sessionStorage;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
/**
|
|
384
|
+
* 配置 Session 存储类型
|
|
385
|
+
* @param {Object} config - 存储配置
|
|
386
|
+
* @param {string} [config.type='memory'] - 存储类型: 'memory', 'file'
|
|
387
|
+
* @param {string} [config.baseDir='.agent/sessions'] - 文件存储目录
|
|
388
|
+
*/
|
|
389
|
+
configureSessionStorage(config) {
|
|
390
|
+
this._sessionStorageConfig = config;
|
|
391
|
+
this._sessionStorage = new SessionStorageAdapter(config);
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
/**
|
|
395
|
+
* 获取当前 Session 存储适配器
|
|
396
|
+
* @returns {SessionStorageAdapter}
|
|
397
|
+
*/
|
|
398
|
+
getSessionStorage() {
|
|
399
|
+
return this._getSessionStorage();
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
/**
|
|
403
|
+
* 获取 SessionContext
|
|
404
|
+
* @param {string} sessionId - 会话 ID
|
|
405
|
+
* @returns {SessionContext|null}
|
|
406
|
+
*/
|
|
407
|
+
getSessionContext(sessionId) {
|
|
408
|
+
return this._sessionContexts.get(sessionId) || null;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
/**
|
|
412
|
+
* 获取当前 Session 上下文(从 AsyncLocalStorage 获取)
|
|
413
|
+
* @returns {SessionContext|null}
|
|
414
|
+
*/
|
|
415
|
+
getCurrentSessionContext() {
|
|
416
|
+
const store = sessionStorage.getStore();
|
|
417
|
+
return store?.sessionContext || null;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
/**
|
|
421
|
+
* 获取当前 sessionId(从 AsyncLocalStorage 获取)
|
|
422
|
+
* @returns {string|null}
|
|
423
|
+
*/
|
|
424
|
+
getCurrentSessionId() {
|
|
425
|
+
const store = sessionStorage.getStore();
|
|
426
|
+
return store?.sessionId || null;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
/**
|
|
430
|
+
* 在 Session 上下文中执行函数
|
|
431
|
+
* @param {string} sessionId - 会话 ID
|
|
432
|
+
* @param {Object} options - Session 选项
|
|
433
|
+
* @param {Function} fn - 异步函数
|
|
434
|
+
* @returns {Promise}
|
|
435
|
+
*/
|
|
436
|
+
runInSession(sessionId, options, fn) {
|
|
437
|
+
if (!sessionId) {
|
|
438
|
+
// 无 session,直接执行
|
|
439
|
+
return fn();
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
const sessionCtx = this.getOrCreateSessionContext(sessionId, options);
|
|
443
|
+
const context = { sessionContext: sessionCtx, sessionId };
|
|
444
|
+
return sessionStorage.run(context, fn);
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
/**
|
|
448
|
+
* 销毁 Session Context
|
|
449
|
+
* @param {string} sessionId
|
|
450
|
+
*/
|
|
451
|
+
destroySessionContext(sessionId) {
|
|
452
|
+
if (this._sessionContexts.has(sessionId)) {
|
|
453
|
+
const ctx = this._sessionContexts.get(sessionId);
|
|
454
|
+
ctx.destroy();
|
|
455
|
+
this._sessionContexts.delete(sessionId);
|
|
456
|
+
|
|
457
|
+
// 从存储中删除
|
|
458
|
+
if (this._sessionStorage) {
|
|
459
|
+
this._sessionStorage.delete(sessionId).catch(() => {});
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
this.emit('session:context-destroyed', ctx);
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
/**
|
|
467
|
+
* 列出所有活跃的 Session ID
|
|
468
|
+
* @returns {string[]}
|
|
469
|
+
*/
|
|
470
|
+
listActiveSessions() {
|
|
471
|
+
return Array.from(this._sessionContexts.keys());
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
// ============================================================================
|
|
475
|
+
// 分层上下文系统 - Request Context
|
|
476
|
+
// ============================================================================
|
|
477
|
+
|
|
478
|
+
/**
|
|
479
|
+
* 获取当前 Request 上下文
|
|
480
|
+
* @returns {Object|null}
|
|
481
|
+
*/
|
|
482
|
+
getRequestContext() {
|
|
483
|
+
return requestStorage.getStore() || null;
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
/**
|
|
487
|
+
* 在 Request 上下文中执行函数
|
|
488
|
+
* @param {Object} context - Request 上下文
|
|
489
|
+
* @param {Function} fn - 异步函数
|
|
490
|
+
* @returns {Promise}
|
|
491
|
+
*/
|
|
492
|
+
runWithRequestContext(context, fn) {
|
|
493
|
+
return requestStorage.run(context, fn);
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
// ============================================================================
|
|
497
|
+
// 分层上下文系统 - Agent Context
|
|
498
|
+
// ============================================================================
|
|
499
|
+
|
|
500
|
+
/**
|
|
501
|
+
* 获取当前 Agent 上下文
|
|
502
|
+
* @returns {Object|null}
|
|
503
|
+
*/
|
|
504
|
+
getAgentContext() {
|
|
505
|
+
return agentStorage.getStore()?.agentContext || null;
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
/**
|
|
509
|
+
* 在 Agent 上下文中执行函数
|
|
510
|
+
* @param {string} agentId - Agent ID
|
|
511
|
+
* @param {Object} context - Agent 上下文
|
|
512
|
+
* @param {Function} fn - 异步函数
|
|
513
|
+
* @returns {Promise}
|
|
514
|
+
*/
|
|
515
|
+
runWithAgentContext(agentId, context, fn) {
|
|
516
|
+
return agentStorage.run({ agentId, ...context }, fn);
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
// ============================================================================
|
|
520
|
+
// 组合上下文
|
|
521
|
+
// ============================================================================
|
|
522
|
+
|
|
523
|
+
/**
|
|
524
|
+
* 在完整上下文中执行(Request + Session)
|
|
525
|
+
* @param {Object} requestOptions - Request 选项
|
|
526
|
+
* @param {string} sessionId - Session ID
|
|
527
|
+
* @param {Function} fn - 异步函数
|
|
528
|
+
* @returns {Promise}
|
|
529
|
+
*/
|
|
530
|
+
async runInContext(requestOptions, sessionId, fn) {
|
|
531
|
+
const sessionCtx = sessionId ? this.getOrCreateSessionContext(sessionId) : null;
|
|
532
|
+
|
|
533
|
+
return requestStorage.run(requestOptions, () => {
|
|
534
|
+
if (sessionCtx) {
|
|
535
|
+
return sessionStorage.run({ sessionContext: sessionCtx, sessionId }, fn);
|
|
536
|
+
}
|
|
537
|
+
return fn();
|
|
538
|
+
});
|
|
539
|
+
}
|
|
540
|
+
|
|
286
541
|
/**
|
|
287
542
|
* 创建 Agent
|
|
288
543
|
* @param {Object} config - Agent 配置
|
|
@@ -484,10 +739,16 @@ class Framework extends EventEmitter {
|
|
|
484
739
|
}
|
|
485
740
|
|
|
486
741
|
/**
|
|
487
|
-
*
|
|
742
|
+
* 销毁框架
|
|
488
743
|
*/
|
|
489
744
|
async destroy() {
|
|
490
|
-
//
|
|
745
|
+
// 刷新所有 Session 存储
|
|
746
|
+
if (this._sessionStorage) {
|
|
747
|
+
await this._sessionStorage.flush();
|
|
748
|
+
await this._sessionStorage.destroy();
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
// 卸载所有插件
|
|
491
752
|
const plugins = this.pluginManager.getAll();
|
|
492
753
|
for (const { name } of plugins) {
|
|
493
754
|
await this.pluginManager.unload(name);
|
|
@@ -215,6 +215,13 @@ class PluginManager {
|
|
|
215
215
|
instance: pluginInstance,
|
|
216
216
|
status: 'registered',
|
|
217
217
|
enabled,
|
|
218
|
+
// 记录插件来源路径(用于热重载)
|
|
219
|
+
// 尝试从实例、prototype 或原始 plugin 对象获取
|
|
220
|
+
sourcePath:
|
|
221
|
+
pluginInstance._sourcePath ||
|
|
222
|
+
(plugin.prototype && plugin.prototype._sourcePath) ||
|
|
223
|
+
plugin._sourcePath ||
|
|
224
|
+
null,
|
|
218
225
|
});
|
|
219
226
|
|
|
220
227
|
this.framework.emit('plugin:registered', pluginInstance);
|
|
@@ -284,6 +291,11 @@ class PluginManager {
|
|
|
284
291
|
return pluginInstance;
|
|
285
292
|
}
|
|
286
293
|
|
|
294
|
+
// 如果 entry 没有 sourcePath,尝试从 plugin prototype 获取(loadPlugin 直接传入类的情况)
|
|
295
|
+
if (!entry.sourcePath && typeof plugin === 'function' && plugin.prototype) {
|
|
296
|
+
entry.sourcePath = plugin.prototype._sourcePath || null;
|
|
297
|
+
}
|
|
298
|
+
|
|
287
299
|
// 调用 install
|
|
288
300
|
try {
|
|
289
301
|
await entry.instance.install(this.framework);
|
|
@@ -476,6 +488,19 @@ class PluginManager {
|
|
|
476
488
|
plugin = pluginModule;
|
|
477
489
|
}
|
|
478
490
|
|
|
491
|
+
// 记录插件源路径(用于热重载)
|
|
492
|
+
let sourcePathSet = false;
|
|
493
|
+
if (typeof plugin === 'function' && plugin.prototype) {
|
|
494
|
+
plugin.prototype._sourcePath = pluginPath;
|
|
495
|
+
sourcePathSet = true;
|
|
496
|
+
} else if (typeof plugin === 'function') {
|
|
497
|
+
plugin._sourcePath = pluginPath;
|
|
498
|
+
sourcePathSet = true;
|
|
499
|
+
}
|
|
500
|
+
if (sourcePathSet) {
|
|
501
|
+
this._log.debug(`Set _sourcePath for plugin ${pluginName}: ${pluginPath}`);
|
|
502
|
+
}
|
|
503
|
+
|
|
479
504
|
// 获取插件名称
|
|
480
505
|
let resolvedPluginName;
|
|
481
506
|
try {
|
|
@@ -639,8 +664,60 @@ class PluginManager {
|
|
|
639
664
|
await entry.instance.stopBot();
|
|
640
665
|
}
|
|
641
666
|
}
|
|
642
|
-
|
|
643
|
-
|
|
667
|
+
|
|
668
|
+
// 如果有 sourcePath,重新 require 文件获取最新代码
|
|
669
|
+
if (entry.sourcePath) {
|
|
670
|
+
this._log.info(`Re-requiring plugin '${name}' from ${entry.sourcePath}`);
|
|
671
|
+
// 清除 require 缓存
|
|
672
|
+
delete require.cache[require.resolve(entry.sourcePath)];
|
|
673
|
+
const pluginModule = require(entry.sourcePath);
|
|
674
|
+
|
|
675
|
+
let NewPluginClass = pluginModule;
|
|
676
|
+
if (pluginModule.default) {
|
|
677
|
+
NewPluginClass = pluginModule.default;
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
// 创建新实例
|
|
681
|
+
let newInstance;
|
|
682
|
+
if (typeof NewPluginClass === 'function') {
|
|
683
|
+
if (NewPluginClass.prototype instanceof require('./plugin-base')) {
|
|
684
|
+
newInstance = new NewPluginClass();
|
|
685
|
+
} else {
|
|
686
|
+
newInstance = NewPluginClass();
|
|
687
|
+
}
|
|
688
|
+
} else {
|
|
689
|
+
newInstance = NewPluginClass;
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
// 复制 sourcePath 到新实例(prototype 上的属性不会自动继承)
|
|
693
|
+
if (entry.sourcePath) {
|
|
694
|
+
newInstance._sourcePath = entry.sourcePath;
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
// 复制 config 到新实例(用户配置需要保留)
|
|
698
|
+
if (entry.instance && entry.instance.config) {
|
|
699
|
+
newInstance.config = { ...entry.instance.config };
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
// 替换旧实例
|
|
703
|
+
entry.instance = newInstance;
|
|
704
|
+
|
|
705
|
+
// 调用 install
|
|
706
|
+
await newInstance.install(this.framework);
|
|
707
|
+
|
|
708
|
+
// 调用 start
|
|
709
|
+
await newInstance.start(this.framework);
|
|
710
|
+
newInstance._started = true;
|
|
711
|
+
|
|
712
|
+
// 通知监听器(如 extension-executor-plugin)重新扫描
|
|
713
|
+
this.framework.emit('plugin:reloaded', newInstance);
|
|
714
|
+
|
|
715
|
+
this._log.info(`Plugin '${name}' re-required from ${entry.sourcePath}`);
|
|
716
|
+
} else {
|
|
717
|
+
this._log.info(`No sourcePath for plugin '${name}', calling reload() only`);
|
|
718
|
+
// 没有 sourcePath,只调用 reload
|
|
719
|
+
await this.reload(name);
|
|
720
|
+
}
|
|
644
721
|
} catch (err) {
|
|
645
722
|
this._log.error(`Enable/reload failed for '${name}':`, err.message);
|
|
646
723
|
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RequestContext - Request 级别的上下文封装
|
|
3
|
+
*
|
|
4
|
+
* 职责:
|
|
5
|
+
* 1. 追踪请求链路(requestId, traceId)
|
|
6
|
+
* 2. 管理请求级超时
|
|
7
|
+
* 3. 传递请求元数据
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const { uuid } = require('../utils/id');
|
|
11
|
+
|
|
12
|
+
class RequestContext {
|
|
13
|
+
/**
|
|
14
|
+
* @param {Object} options - 配置选项
|
|
15
|
+
* @param {string} [options.requestId] - 请求 ID,默认自动生成
|
|
16
|
+
* @param {string} [options.traceId] - 追踪 ID,默认自动生成
|
|
17
|
+
* @param {string} [options.parentSpanId] - 父 span ID
|
|
18
|
+
* @param {number} [options.timeout] - 超时时间(毫秒),默认 120000
|
|
19
|
+
* @param {boolean} [options.isStream] - 是否为流式请求
|
|
20
|
+
* @param {string} [options.userId] - 用户标识
|
|
21
|
+
*/
|
|
22
|
+
constructor(options = {}) {
|
|
23
|
+
this.requestId = options.requestId || `req_${uuid()}`;
|
|
24
|
+
this.traceId = options.traceId || `trace_${uuid()}`;
|
|
25
|
+
this.traceId = options.traceId || generateId();
|
|
26
|
+
this.parentSpanId = options.parentSpanId || null;
|
|
27
|
+
|
|
28
|
+
this.timeout = options.timeout || 120000; // 默认 2 分钟
|
|
29
|
+
this.startTime = options.startTime || Date.now();
|
|
30
|
+
|
|
31
|
+
this.isStream = options.isStream || false;
|
|
32
|
+
this.userId = options.userId || null;
|
|
33
|
+
|
|
34
|
+
// 请求级状态
|
|
35
|
+
this.cancelled = false;
|
|
36
|
+
this.abortController = null;
|
|
37
|
+
|
|
38
|
+
// 元数据
|
|
39
|
+
this.metadata = options.metadata || {};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* 创建 AbortController(延迟初始化)
|
|
44
|
+
* @returns {AbortController}
|
|
45
|
+
*/
|
|
46
|
+
getAbortController() {
|
|
47
|
+
if (!this.abortController) {
|
|
48
|
+
this.abortController = new AbortController();
|
|
49
|
+
}
|
|
50
|
+
return this.abortController;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* 检查是否超时
|
|
55
|
+
* @returns {boolean}
|
|
56
|
+
*/
|
|
57
|
+
isTimeout() {
|
|
58
|
+
return Date.now() - this.startTime > this.timeout;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* 取消请求
|
|
63
|
+
*/
|
|
64
|
+
cancel() {
|
|
65
|
+
this.cancelled = true;
|
|
66
|
+
if (this.abortController) {
|
|
67
|
+
this.abortController.abort();
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* 获取请求耗时(毫秒)
|
|
73
|
+
* @returns {number}
|
|
74
|
+
*/
|
|
75
|
+
getElapsed() {
|
|
76
|
+
return Date.now() - this.startTime;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* 获取请求摘要
|
|
81
|
+
* @returns {Object}
|
|
82
|
+
*/
|
|
83
|
+
toJSON() {
|
|
84
|
+
return {
|
|
85
|
+
requestId: this.requestId,
|
|
86
|
+
traceId: this.traceId,
|
|
87
|
+
parentSpanId: this.parentSpanId,
|
|
88
|
+
timeout: this.timeout,
|
|
89
|
+
startTime: this.startTime,
|
|
90
|
+
elapsed: this.getElapsed(),
|
|
91
|
+
isStream: this.isStream,
|
|
92
|
+
userId: this.userId,
|
|
93
|
+
cancelled: this.cancelled,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
module.exports = { RequestContext };
|