foliko 1.0.77 → 1.0.78

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.
Files changed (36) hide show
  1. package/.agent/data/default.json +31559 -0
  2. package/.agent/data/plugins-state.json +10 -1
  3. package/.claude/settings.local.json +13 -2
  4. package/.env.example +54 -54
  5. package/cli/src/commands/chat.js +1 -1
  6. package/examples/basic.js +1 -1
  7. package/package.json +5 -3
  8. package/plugins/ai-plugin.js +1 -1
  9. package/plugins/ambient-agent/index.js +1 -1
  10. package/plugins/audit-plugin.js +1 -1
  11. package/plugins/default-plugins.js +92 -209
  12. package/plugins/email/index.js +1 -1
  13. package/plugins/extension-executor-plugin.js +326 -0
  14. package/plugins/feishu-plugin.js +1 -1
  15. package/plugins/file-system-plugin.js +57 -6
  16. package/plugins/gate-trading.js +747 -0
  17. package/plugins/install-plugin.js +1 -1
  18. package/plugins/python-executor-plugin.js +1 -1
  19. package/plugins/python-plugin-loader.js +275 -105
  20. package/plugins/rules-plugin.js +1 -1
  21. package/plugins/scheduler-plugin.js +1 -1
  22. package/plugins/session-plugin.js +132 -7
  23. package/plugins/shell-executor-plugin.js +1 -1
  24. package/plugins/storage-plugin.js +24 -1
  25. package/plugins/subagent-plugin.js +2 -2
  26. package/plugins/telegram-plugin.js +1 -1
  27. package/plugins/think-plugin.js +10 -10
  28. package/plugins/tools-plugin.js +1 -1
  29. package/plugins/web-plugin.js +49 -18
  30. package/plugins/weixin-plugin.js +1 -1
  31. package/skills/foliko-dev/SKILL.md +583 -500
  32. package/skills/python-plugin-dev/SKILL.md +238 -266
  33. package/src/core/agent-chat.js +103 -4
  34. package/src/core/agent.js +85 -19
  35. package/src/core/plugin-base.js +43 -0
  36. package/src/executors/mcp-executor.js +126 -22
@@ -57,7 +57,8 @@
57
57
  "maxSessions": 100,
58
58
  "maxHistoryLength": 150,
59
59
  "autoCleanup": true,
60
- "cleanupInterval": 300000
60
+ "cleanupInterval": 300000,
61
+ "persistToStorage": true
61
62
  }
62
63
  },
63
64
  "audit": {
@@ -251,5 +252,13 @@
251
252
  "agentsDir": "D:\\code\\vb-agent\\.agent\\agents",
252
253
  "agents": []
253
254
  }
255
+ },
256
+ "extension-executor": {
257
+ "enabled": true,
258
+ "config": {}
259
+ },
260
+ "gate-trading": {
261
+ "enabled": true,
262
+ "config": {}
254
263
  }
255
264
  }
@@ -152,8 +152,19 @@
152
152
  "Bash(git checkout:*)",
153
153
  "Bash(node --check plugins/default-plugins.js && echo \"OK\")",
154
154
  "Bash(node --check plugins/subagent-plugin.js && echo \"OK\")",
155
- "Bash(head -1 \"D:\\\\code\\\\vb-agent\\\\cli\\\\bin\\\\foliko.js\" | xxd)",
156
- "Bash(node D:/code/vb-agent/cli/bin/foliko.js chat)"
155
+ "Bash(cd D:/Code/vb-agent && pnpm install 2>&1 | tail -10)",
156
+ "Bash(mkdir -p \"D:/Code/vb-agent/node_modules/@chanak\" && ln -sf \"../../.pnpm/@chnak+zod-to-markdown@1.0.1/node_modules/@chnak/zod-to-markdown\" \"D:/Code/vb-agent/node_modules/@chnak/zod-to-markdown\")",
157
+ "Bash(cd D:/Code/vb-agent && rm -rf node_modules/@chanak && npm install @chnak/zod-to-markdown --legacy-peer-deps 2>&1 | tail -10)",
158
+ "Bash(cd D:/Code/vb-agent && npm start 2>&1 | head -30)",
159
+ "Bash(cd D:/Code/vb-agent && npm start 2>&1 | head -40)",
160
+ "Bash(grep -l \"module.exports\" D:/Code/vb-agent/plugins/*.js 2>/dev/null | head -20)",
161
+ "Bash(grep -E \"^module.exports\" D:/Code/vb-agent/plugins/*.js | head -30)",
162
+ "Bash(grep -E \"^module.exports\" D:/Code/vb-agent/src/executors/*.js 2>/dev/null)",
163
+ "Bash(grep \"module.exports\" D:/Code/vb-agent/plugins/ambient-agent/*.js 2>/dev/null)",
164
+ "Bash(grep -E \"^module.exports\" D:/Code/vb-agent/plugins/*.js | head -20)",
165
+ "Bash(cd D:/Code/vb-agent && npm start 2>&1 | head -25)",
166
+ "Bash(cd D:/Code/vb-agent && timeout 20 npm start 2>&1 | head -35)",
167
+ "Bash(node -c D:/Code/vb-agent/plugins/gate-trading.js 2>&1)"
157
168
  ]
158
169
  }
159
170
  }
package/.env.example CHANGED
@@ -1,56 +1,56 @@
1
1
  # ========== AI Configuration ==========
2
2
  # 最大输出 tokens(影响 AI 回复长度)
3
- MAX_OUTPUT_TOKENS=8192
4
- # AI Provider: minimax, deepseek, openai, anthropic 等
5
- FOLIKO_PROVIDER=minimax
6
-
7
- # AI Model(如果未设置,使用 provider 默认值)
8
- # MiniMax: MiniMax-M2.7
9
- # DeepSeek: deepseek-chat, deepseek-coder 等
10
- FOLIKO_MODEL=MiniMax-M2.7
11
-
12
- # API Base URL(如果未设置,使用 provider 默认值)
13
- # MiniMax: https://api.minimaxi.com/v1
14
- # DeepSeek: https://api.deepseek.com/v1
15
- FOLIKO_BASE_URL=https://api.minimaxi.com/v1
16
-
17
- # API Key(通用,如果未设置则尝试 provider 专用 key)
18
- FOLIKO_API_KEY=sk-your-api-key
19
-
20
- # Provider 专用 API Key(可选,如果 FOLIKO_API_KEY 未设置则使用这些)
21
- DEEPSEEK_API_KEY=sk-your-deepseek-api-key
22
- MINIMAX_API_KEY=sk-your-minimax-api-key
23
-
24
- # ========== Email Configuration ==========
25
- # SMTP Settings (for sending emails)
26
- SMTP_HOST=smtp.gmail.com
27
- SMTP_PORT=587
28
- SMTP_SECURE=false
29
- SMTP_USER=your-email@gmail.com
30
- SMTP_PASS=your-app-password
31
-
32
- # IMAP Settings (for reading emails)
33
- IMAP_HOST=imap.gmail.com
34
- IMAP_PORT=993
35
- IMAP_USER=your-email@gmail.com
36
- IMAP_PASS=your-app-password
37
-
38
- # Default sender email address
39
- FROM_EMAIL=your-email@gmail.com
40
-
41
- # ========== Telegram Bot (optional) ==========
42
- TELEGRAM_BOT_TOKEN=your-telegram-bot-token
43
-
44
- # ========== Feishu Bot (optional) ==========
45
- FEISHU_APP_ID=cli_xxxxxxxxxxx
46
- FEISHU_APP_SECRET=app_secret
47
-
48
- # ========== Web Server (optional) ==========
49
- # Web 服务端口,默认 8088
50
- WEB_PORT=3000
51
-
52
- # Web 服务主机,默认 127.0.0.1
53
- WEB_HOST=127.0.0.1
54
-
55
- # 公网访问的 base URL(用于生成 webhook URL 等),不设置则使用 host:port,最好部署在docker中可以暴露自定义域名
56
- WEB_BASE_URL=https://your-domain.com
3
+ MAX_OUTPUT_TOKENS=8192
4
+ # AI Provider: minimax, deepseek, openai, anthropic 等
5
+ FOLIKO_PROVIDER=minimax
6
+
7
+ # AI Model(如果未设置,使用 provider 默认值)
8
+ # MiniMax: MiniMax-M2.7
9
+ # DeepSeek: deepseek-chat, deepseek-coder 等
10
+ FOLIKO_MODEL=MiniMax-M2.7
11
+
12
+ # API Base URL(如果未设置,使用 provider 默认值)
13
+ # MiniMax: https://api.minimaxi.com/v1
14
+ # DeepSeek: https://api.deepseek.com/v1
15
+ FOLIKO_BASE_URL=https://api.minimaxi.com/v1
16
+
17
+ # API Key(通用,如果未设置则尝试 provider 专用 key)
18
+ FOLIKO_API_KEY=sk-your-api-key
19
+
20
+ # Provider 专用 API Key(可选,如果 FOLIKO_API_KEY 未设置则使用这些)
21
+ DEEPSEEK_API_KEY=sk-your-deepseek-api-key
22
+ MINIMAX_API_KEY=sk-your-minimax-api-key
23
+
24
+ # ========== Email Configuration ==========
25
+ # SMTP Settings (for sending emails)
26
+ SMTP_HOST=smtp.gmail.com
27
+ SMTP_PORT=587
28
+ SMTP_SECURE=false
29
+ SMTP_USER=your-email@gmail.com
30
+ SMTP_PASS=your-app-password
31
+
32
+ # IMAP Settings (for reading emails)
33
+ IMAP_HOST=imap.gmail.com
34
+ IMAP_PORT=993
35
+ IMAP_USER=your-email@gmail.com
36
+ IMAP_PASS=your-app-password
37
+
38
+ # Default sender email address
39
+ FROM_EMAIL=your-email@gmail.com
40
+
41
+ # ========== Telegram Bot (optional) ==========
42
+ TELEGRAM_BOT_TOKEN=your-telegram-bot-token
43
+
44
+ # ========== Feishu Bot (optional) ==========
45
+ FEISHU_APP_ID=cli_xxxxxxxxxxx
46
+ FEISHU_APP_SECRET=app_secret
47
+
48
+ # ========== Web Server (optional) ==========
49
+ # Web 服务端口,默认 8088
50
+ WEB_PORT=3000
51
+
52
+ # Web 服务主机,默认 127.0.0.1
53
+ WEB_HOST=127.0.0.1
54
+
55
+ # 公网访问的 base URL(用于生成 webhook URL 等),不设置则使用 host:port,最好部署在docker中可以暴露自定义域名
56
+ WEB_BASE_URL=https://your-domain.com
@@ -94,7 +94,7 @@ async function chatCommand(args) {
94
94
  baseURL: options.baseURL,
95
95
  apiKey: options.apiKey,
96
96
  providerOptions: {
97
- maxOutputTokens: parseInt(process.env.MAX_OUTPUT_TOKENS) || 8192,
97
+ maxOutputTokens: 8192,
98
98
  },
99
99
  },
100
100
  });
package/examples/basic.js CHANGED
@@ -4,7 +4,7 @@
4
4
  */
5
5
 
6
6
  const { Framework } = require('../src');
7
- const { AIPlugin } = require('../plugins/ai-plugin');
7
+ const AIPlugin = require('../plugins/ai-plugin');
8
8
  const { z } = require('zod');
9
9
 
10
10
  async function main() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "foliko",
3
- "version": "1.0.77",
3
+ "version": "1.0.78",
4
4
  "description": "简约的插件化 Agent 框架",
5
5
  "main": "src/index.js",
6
6
  "type": "commonjs",
@@ -54,24 +54,26 @@
54
54
  "@ai-sdk/openai-compatible": "^2.0.35",
55
55
  "@anthropic-ai/sdk": "^0.39.0",
56
56
  "@chnak/weixin-bot": "^1.2.1",
57
+ "@chnak/zod-to-markdown": "^1.0.1",
57
58
  "@hono/node-server": "^1.19.11",
58
59
  "@larksuiteoapi/node-sdk": "^1.59.0",
59
60
  "@modelcontextprotocol/sdk": "^1.27.1",
60
61
  "ai": "^6.0.116",
61
62
  "dotenv": "^17.3.1",
63
+ "gate-api": "^7.2.57",
62
64
  "hono": "^4.12.9",
63
65
  "imap-mkl": "^1.0.2",
64
66
  "mailparser": "^3.7.2",
65
67
  "marked": "^11.2.0",
66
68
  "marked-terminal": "6",
67
69
  "node-cron": "^4.2.1",
70
+ "node-html-markdown": "^1.3.0",
68
71
  "node-telegram-bot-api": "^0.67.0",
69
72
  "nodemailer": "^6.10.0",
70
73
  "qrcode-terminal": "^0.12.0",
71
74
  "remove-markdown": "^0.6.3",
72
- "tiktoken": "^1.0.22",
73
75
  "vm2": "^3.10.5",
74
- "zod": "^3.24.0"
76
+ "zod": "^3.25.76"
75
77
  },
76
78
  "devDependencies": {
77
79
  "husky": "^9.1.7",
@@ -99,4 +99,4 @@ class AIPlugin extends Plugin {
99
99
  }
100
100
  }
101
101
 
102
- module.exports = { AIPlugin }
102
+ module.exports = AIPlugin
@@ -576,4 +576,4 @@ ${eventList}
576
576
  }
577
577
  }
578
578
 
579
- module.exports = { AmbientAgentPlugin, GoalState }
579
+ module.exports = AmbientAgentPlugin
@@ -185,4 +185,4 @@ class AuditPlugin extends Plugin {
185
185
  }
186
186
  }
187
187
 
188
- module.exports = { AuditPlugin }
188
+ module.exports = AuditPlugin
@@ -229,7 +229,7 @@ async function bootstrapDefaults(framework, config = {}) {
229
229
  // 核心插件列表(不能禁用,必须加载)
230
230
  const CORE_PLUGINS = new Set([
231
231
  'install', 'ai', 'storage', 'tools', 'workflow', 'skill-manager',
232
- 'mcp-executor', 'shell-executor', 'python-executor', 'session', 'web',
232
+ 'mcp-executor', 'extension-executor', 'shell-executor', 'python-executor', 'session', 'web',
233
233
  'audit', 'rules', 'scheduler', 'file-system', 'think', 'ambient',
234
234
  'python-plugin-loader', 'telegram', 'weixin', 'subagent-manager'
235
235
  ])
@@ -257,7 +257,7 @@ async function bootstrapDefaults(framework, config = {}) {
257
257
 
258
258
  // 0. Install 工具插件(最先加载,让其他插件能用到它的 node_modules)
259
259
  if (shouldLoad('install')) {
260
- const { InstallPlugin } = require('./install-plugin')
260
+ const InstallPlugin = require('./install-plugin')
261
261
  await framework.loadPlugin(new InstallPlugin({ agentDir: agentConfig.agentDir }))
262
262
  framework._debug&&bootstrapLog.debug(' Install Plugin loaded')
263
263
  }
@@ -273,7 +273,7 @@ async function bootstrapDefaults(framework, config = {}) {
273
273
  if (!shouldLoad('ai') || !(aiConfig.provider || aiConfig.model || aiConfig.apiKey)) {
274
274
  // 跳过或已禁用
275
275
  } else {
276
- const { AIPlugin } = require('./ai-plugin')
276
+ const AIPlugin = require('./ai-plugin')
277
277
  // 根据 provider 获取对应的 API key
278
278
  const upperProvider = (aiConfig.provider || 'deepseek').toUpperCase().replace(/-/g, '_')
279
279
  const envApiKey = process.env[`${upperProvider}_API_KEY`] || process.env.FOLIKO_API_KEY
@@ -306,38 +306,14 @@ async function bootstrapDefaults(framework, config = {}) {
306
306
  framework._debug&&bootstrapLog.debug(' Main Agent created, has _chatHandler:', !!framework._mainAgent._chatHandler)
307
307
  }
308
308
 
309
- // 2. Storage 存储插件
310
- if (shouldLoad('storage')) {
311
- const { StoragePlugin } = require('./storage-plugin')
312
- await framework.loadPlugin(new StoragePlugin())
313
- framework._debug&&bootstrapLog.debug(' Storage Plugin loaded')
309
+ // 2. Skill 管理器插件(需要 skillsDirs 配置)
310
+ if (shouldLoad('skill-manager') && skillsDirs.length > 0) {
311
+ const { SkillManagerPlugin } = require('../src/capabilities/skill-manager')
312
+ await framework.loadPlugin(new SkillManagerPlugin({ skillsDirs }))
313
+ framework._debug&&bootstrapLog.debug(' Skill Manager loaded')
314
314
  }
315
315
 
316
- // 3. 内置工具插件
317
- if (shouldLoad('tools')) {
318
- const { ToolsPlugin } = require('./tools-plugin')
319
- await framework.loadPlugin(new ToolsPlugin())
320
- framework._debug&&bootstrapLog.debug(' Tools Plugin loaded')
321
- }
322
-
323
- // 4. 工作流插件
324
- if (shouldLoad('workflow')) {
325
- const { WorkflowPlugin } = require('../src/capabilities/workflow-engine')
326
- await framework.loadPlugin(new WorkflowPlugin())
327
- framework._debug&&bootstrapLog.debug(' Workflow Plugin loaded')
328
- }
329
-
330
- // 5. Skill 管理器插件
331
- if (shouldLoad('skill-manager')) {
332
- if (skillsDirs.length > 0) {
333
- const { SkillManagerPlugin } = require('../src/capabilities/skill-manager')
334
- // 传递所有 skills 目录
335
- await framework.loadPlugin(new SkillManagerPlugin({ skillsDirs }))
336
- framework._debug&&bootstrapLog.debug(' Skill Manager loaded')
337
- }
338
- }
339
-
340
- // 6. MCP 执行器插件(始终加载,确保 mcp_reload 工具可用)
316
+ // 3. MCP 执行器插件(需要转换 mcpServers 格式)
341
317
  if (shouldLoad('mcp-executor')) {
342
318
  const mcpServers = Object.entries(agentConfig.mcpServers || {})
343
319
  const servers = mcpServers.map(([name, cfg]) => ({
@@ -354,152 +330,21 @@ async function bootstrapDefaults(framework, config = {}) {
354
330
  framework._debug&&bootstrapLog.debug(` MCP Executor loaded${servers.length > 0 ? ` (${servers.length} servers)` : ' (no servers)'}`)
355
331
  }
356
332
 
357
- // 7. Shell 执行器插件
358
- if (shouldLoad('shell-executor')) {
359
- const { ShellExecutorPlugin } = require('./shell-executor-plugin')
360
- await framework.loadPlugin(new ShellExecutorPlugin())
361
- framework._debug&&bootstrapLog.debug(' Shell Executor loaded')
362
- }
363
-
364
- // 8. Python 执行器插件
365
- if (shouldLoad('python-executor')) {
366
- const { PythonExecutorPlugin } = require('./python-executor-plugin')
367
- await framework.loadPlugin(new PythonExecutorPlugin())
368
- framework._debug&&bootstrapLog.debug(' Python Executor loaded')
369
- }
370
-
371
- // 8.5 Web Web服务插件
372
- if (shouldLoad('web')) {
373
- const { WebPlugin } = require('./web-plugin')
374
- await framework.loadPlugin(new WebPlugin())
375
- framework._debug&&bootstrapLog.debug(' Web Plugin loaded')
376
- }
377
-
378
- // 9. Session 会话管理插件
379
- if (shouldLoad('session')) {
380
- const { SessionPlugin } = require('./session-plugin')
381
- await framework.loadPlugin(new SessionPlugin())
382
- framework._debug&&bootstrapLog.debug(' Session Plugin loaded')
383
- }
384
-
385
- // 10. Audit 审计日志插件
386
- if (shouldLoad('audit')) {
387
- const { AuditPlugin } = require('./audit-plugin')
388
- await framework.loadPlugin(new AuditPlugin())
389
- framework._debug&&bootstrapLog.debug(' Audit Plugin loaded')
390
- }
391
-
392
- // 10. Rules 规则引擎插件
393
- if (shouldLoad('rules')) {
394
- const { RulesPlugin } = require('./rules-plugin')
395
- await framework.loadPlugin(new RulesPlugin())
396
- framework._debug&&bootstrapLog.debug(' Rules Plugin loaded')
397
- }
398
-
399
- // 11. Scheduler 定时任务插件
400
- if (shouldLoad('scheduler')) {
401
- const { SchedulerPlugin } = require('./scheduler-plugin')
402
- await framework.loadPlugin(new SchedulerPlugin())
403
- framework._debug&&bootstrapLog.debug(' Scheduler Plugin loaded')
404
- }
405
-
406
- // 11. FileSystem 文件系统插件
407
- if (shouldLoad('file-system')) {
408
- const { FileSystemPlugin } = require('./file-system-plugin')
409
- await framework.loadPlugin(new FileSystemPlugin())
410
- framework._debug&&bootstrapLog.debug(' FileSystem Plugin loaded')
411
- }
412
-
413
- // 12. Think 主动思考插件
414
- if (shouldLoad('think')) {
415
- const { ThinkPlugin } = require('./think-plugin')
416
- await framework.loadPlugin(new ThinkPlugin())
417
- framework._debug&&bootstrapLog.debug(' Think Plugin loaded')
418
- }
419
-
420
- // 12.1 Ambient Agent 插件
421
- if (shouldLoad('ambient')) {
422
- const { AmbientAgentPlugin } = require('./ambient-agent')
423
- await framework.loadPlugin(new AmbientAgentPlugin())
424
- framework._debug&&bootstrapLog.debug(' Ambient Agent Plugin loaded')
425
- }
426
-
427
- // 12.5 Python 插件加载器
333
+ // 4. Python 插件加载器(需要 agentDir)
428
334
  if (shouldLoad('python-plugin-loader')) {
429
- const { PythonPluginLoader } = require('./python-plugin-loader')
335
+ const PythonPluginLoader = require('./python-plugin-loader')
430
336
  await framework.loadPlugin(new PythonPluginLoader({ agentDir: agentConfig.agentDir }))
431
337
  framework._debug&&bootstrapLog.debug(' Python Plugin Loader loaded')
432
338
  }
433
339
 
434
- // 12.6 Telegram 插件(默认禁用,需要在 plugins.json 中设置 enabled: true
435
- if (shouldLoad('telegram')) {
436
- const { TelegramPlugin } = require('./telegram-plugin')
437
- const telegramConfig = {
438
- botToken: process.env.TELEGRAM_BOT_TOKEN || agentConfig.telegram?.botToken
439
- }
440
- await framework.loadPlugin(new TelegramPlugin(telegramConfig))
441
- }
442
-
443
- // 12.7 WeChat 插件(默认禁用,需要在 plugins.json 中设置 enabled: true)
444
- if (shouldLoad('weixin')) {
445
- try {
446
- const { WeixinPlugin } = require('./weixin-plugin')
447
- const weixinConfig = {
448
- forceLogin: agentConfig.weixin?.forceLogin || false,
449
- qrcodeTerminal: agentConfig.weixin?.qrcodeTerminal !== false,
450
- allowedUsers: agentConfig.weixin?.allowedUsers || []
451
- }
452
- await framework.loadPlugin(new WeixinPlugin(weixinConfig))
453
- } catch (err) {
454
- bootstrapLog.warn(' WeChat Plugin not available:', err.message)
455
- }
456
- }
457
-
458
- // 12.8 Email 插件(默认禁用,需要在 plugins.json 中设置 enabled: true)
459
- if (shouldLoad('email')) {
460
- try {
461
- const { Plugin } = require('../src/core/plugin-base')
462
- const { logger } = require('../src/utils/logger')
463
- const log = logger.child('AgentConfig')
464
- const bootstrapLog = logger.child('Bootstrap')
465
- const { EmailPlugin } = require('./email')
466
- // 支持两种格式:email.smtp 或 email.config.smtp
467
- const emailData = agentConfig.email || {}
468
- const emailCfg = emailData.config || {}
469
- const emailConfig = {
470
- smtp: emailCfg.smtp || emailData.smtp || {},
471
- imap: emailCfg.imap || emailData.imap || {},
472
- clientId: emailCfg.clientId || emailData.clientId
473
- }
474
- await framework.loadPlugin(new EmailPlugin(emailConfig))
475
- } catch (err) {
476
- bootstrapLog.warn(' Email Plugin not available:', err.message)
477
- }
478
- }
479
-
480
- // 12.9 Feishu 插件(默认禁用,需要在 plugins.json 中设置 enabled: true)
481
- if (shouldLoad('feishu')) {
482
- try {
483
- const { FeishuPlugin } = require('./feishu-plugin')
484
- const feishuConfig = {
485
- allowedUsers: agentConfig.feishu?.allowedUsers || []
486
- }
487
- await framework.loadPlugin(new FeishuPlugin(feishuConfig))
488
- } catch (err) {
489
- bootstrapLog.warn(' Feishu Plugin not available:', err.message)
490
- }
491
- }
492
-
493
- // 12.10 SubAgent 管理器
340
+ // 5. SubAgent 管理器(需要特殊初始化 SubAgentConfigManager
494
341
  if (shouldLoad('subagent-manager')) {
495
342
  try {
496
- // 先初始化 SubAgentConfigManager 并加载所有配置
497
343
  const { SubAgentConfigManager } = require('../src/core/sub-agent-config')
498
344
  const subAgentConfigManager = new SubAgentConfigManager(agentConfig.agentsDir)
499
345
  subAgentConfigManager.loadAll()
500
346
  framework._subAgentConfigManager = subAgentConfigManager
501
347
 
502
- // 然后加载 SubAgentManagerPlugin
503
348
  const { SubAgentManagerPlugin } = require('./subagent-plugin')
504
349
  await framework.loadPlugin(new SubAgentManagerPlugin({ agentsDir: agentConfig.agentsDir }))
505
350
  framework._debug&&bootstrapLog.debug(' SubAgent Manager loaded')
@@ -508,7 +353,7 @@ const bootstrapLog = logger.child('Bootstrap')
508
353
  }
509
354
  }
510
355
 
511
- // 13. 加载自定义插件
356
+ // 6. 自动加载 plugins/ 目录下的所有插件
512
357
  await loadCustomPlugins(framework, agentConfig)
513
358
 
514
359
  framework._debug&&bootstrapLog.debug(' All plugins loaded')
@@ -598,56 +443,94 @@ function scanPluginNames(pluginsDir) {
598
443
  }
599
444
 
600
445
  async function loadCustomPlugins(framework, agentConfig) {
601
- // 加载 .agent/plugins 目录下的自定义插件
602
- const pluginsDir = path.resolve(process.cwd(), '.agent', 'plugins')
446
+ const { Plugin } = require('../src/core/plugin-base')
447
+
448
+ // 加载两个目录下的自定义插件:
449
+ // 1. .agent/plugins/ - 用户自定义插件(强制启用)
450
+ // 2. plugins/ - 项目内置插件(自动加载)
451
+ const dirs = [
452
+ { dir: path.resolve(process.cwd(), '.agent', 'plugins'), forceEnabled: true },
453
+ { dir: path.resolve(process.cwd(), 'plugins'), forceEnabled: false }
454
+ ]
603
455
 
604
- if (!fs.existsSync(pluginsDir)) {
605
- return
606
- }
456
+ for (const { dir, forceEnabled } of dirs) {
457
+ if (!fs.existsSync(dir)) {
458
+ continue
459
+ }
607
460
 
608
- // 扫描所有插件名称(支持文件夹和单文件)
609
- const pluginNames = scanPluginNames(pluginsDir)
461
+ // 扫描所有插件名称(支持文件夹和单文件)
462
+ const pluginNames = scanPluginNames(dir)
610
463
 
611
- for (const pluginName of pluginNames) {
612
- try {
613
- const resolved = resolvePluginPath(pluginsDir, pluginName)
614
- if (!resolved) {
615
- //log.warn(` Cannot resolve plugin: ${pluginName}`)
616
- continue
617
- }
464
+ for (const pluginName of pluginNames) {
465
+ try {
466
+ const resolved = resolvePluginPath(dir, pluginName)
467
+ if (!resolved) {
468
+ continue
469
+ }
618
470
 
619
- const { path: pluginPath, type } = resolved
471
+ const { path: pluginPath, type } = resolved
472
+
473
+ // 清除缓存
474
+ delete require.cache[require.resolve(pluginPath)]
475
+ const pluginModule = require(pluginPath)
476
+
477
+ let plugin
478
+ if (typeof pluginModule === 'function') {
479
+ plugin = pluginModule
480
+ } else if (pluginModule.default) {
481
+ plugin = pluginModule.default
482
+ } else {
483
+ // 支持具名导出 { PluginClass } 或 { PluginClass, Other }
484
+ // 找到第一个是 Plugin 子类的导出
485
+ const keys = Object.keys(pluginModule)
486
+ let foundPlugin = null
487
+ for (const key of keys) {
488
+ const exp = pluginModule[key]
489
+ if (typeof exp === 'function' && exp.prototype instanceof Plugin) {
490
+ foundPlugin = exp
491
+ break
492
+ }
493
+ }
494
+ plugin = foundPlugin || pluginModule
495
+ }
620
496
 
621
- // 清除缓存
622
- delete require.cache[require.resolve(pluginPath)]
623
- const pluginModule = require(pluginPath)
497
+ // 获取插件名称(参考 plugin-manager 的逻辑)
498
+ let resolvedPluginName = pluginName
499
+ if (typeof plugin === 'function' && plugin.prototype instanceof Plugin) {
500
+ // Plugin 子类:实例化获取名称
501
+ const instance = new plugin()
502
+ resolvedPluginName = instance.name || pluginName
503
+ } else if (typeof plugin === 'function') {
504
+ // 工厂函数
505
+ const result = plugin(Plugin)
506
+ resolvedPluginName = result?.name || pluginName
507
+ } else {
508
+ resolvedPluginName = plugin?.name || pluginName
509
+ }
624
510
 
625
- let plugin
626
- if (typeof pluginModule === 'function') {
627
- // 免引入写法:直接传递函数,让 loadPlugin 内部处理
628
- plugin = pluginModule
629
- } else if (pluginModule.default) {
630
- plugin = pluginModule.default
631
- } else {
632
- plugin = pluginModule
633
- }
511
+ // 如果插件已加载且已启动,跳过
512
+ if (framework.pluginManager.has(resolvedPluginName) &&
513
+ framework.pluginManager.get(resolvedPluginName)?._started) {
514
+ continue
515
+ }
634
516
 
635
- // 获取插件名称
636
- const tempPlugin = typeof plugin === 'function' && plugin.prototype?.name
637
- ? new plugin() : plugin
638
- const resolvedPluginName = tempPlugin.name || pluginName
517
+ bootstrapLog.debug(` Loading plugin: ${resolvedPluginName} (${type})`)
639
518
 
640
- // 如果插件已加载且已启动,跳过
641
- if (framework.pluginManager.has(resolvedPluginName) &&
642
- framework.pluginManager.get(resolvedPluginName)?._started) {
643
- continue
644
- }
519
+ // 从 agentConfig 获取插件配置
520
+ const pluginConfig = agentConfig[resolvedPluginName] || {}
645
521
 
646
- //bootstrapLog.debug(` Loading custom plugin: ${pluginName} (${type})`)
647
- // .agent/plugins 目录下的插件强制启用,不受 state 文件 enabled: false 影响
648
- await framework.pluginManager.load(plugin, { forceEnabled: true })
649
- } catch (err) {
650
- log.error(` Failed to load plugin ${pluginName}:`, err.message)
522
+ // 实例化插件并传入配置
523
+ let pluginInstance
524
+ if (typeof plugin === 'function' && plugin.prototype instanceof Plugin) {
525
+ pluginInstance = new plugin(pluginConfig)
526
+ } else {
527
+ pluginInstance = plugin
528
+ }
529
+
530
+ await framework.pluginManager.load(pluginInstance, { forceEnabled })
531
+ } catch (err) {
532
+ log.error(` Failed to load plugin ${pluginName}:`, err.message)
533
+ }
651
534
  }
652
535
  }
653
536
  }
@@ -275,4 +275,4 @@ class EmailPlugin extends Plugin {
275
275
  }
276
276
  }
277
277
 
278
- module.exports = { EmailPlugin }
278
+ module.exports = EmailPlugin