foliko 1.0.74 → 1.0.75

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 (237) hide show
  1. package/.agent/.shared/ui-ux-pro-max/data/charts.csv +26 -0
  2. package/.agent/.shared/ui-ux-pro-max/data/colors.csv +97 -0
  3. package/.agent/.shared/ui-ux-pro-max/data/icons.csv +101 -0
  4. package/.agent/.shared/ui-ux-pro-max/data/landing.csv +31 -0
  5. package/.agent/.shared/ui-ux-pro-max/data/products.csv +97 -0
  6. package/.agent/.shared/ui-ux-pro-max/data/prompts.csv +24 -0
  7. package/.agent/.shared/ui-ux-pro-max/data/react-performance.csv +45 -0
  8. package/.agent/.shared/ui-ux-pro-max/data/stacks/flutter.csv +53 -0
  9. package/.agent/.shared/ui-ux-pro-max/data/stacks/html-tailwind.csv +56 -0
  10. package/.agent/.shared/ui-ux-pro-max/data/stacks/jetpack-compose.csv +53 -0
  11. package/.agent/.shared/ui-ux-pro-max/data/stacks/nextjs.csv +53 -0
  12. package/.agent/.shared/ui-ux-pro-max/data/stacks/nuxt-ui.csv +51 -0
  13. package/.agent/.shared/ui-ux-pro-max/data/stacks/nuxtjs.csv +59 -0
  14. package/.agent/.shared/ui-ux-pro-max/data/stacks/react-native.csv +52 -0
  15. package/.agent/.shared/ui-ux-pro-max/data/stacks/react.csv +54 -0
  16. package/.agent/.shared/ui-ux-pro-max/data/stacks/shadcn.csv +61 -0
  17. package/.agent/.shared/ui-ux-pro-max/data/stacks/svelte.csv +54 -0
  18. package/.agent/.shared/ui-ux-pro-max/data/stacks/swiftui.csv +51 -0
  19. package/.agent/.shared/ui-ux-pro-max/data/stacks/vue.csv +50 -0
  20. package/.agent/.shared/ui-ux-pro-max/data/styles.csv +59 -0
  21. package/.agent/.shared/ui-ux-pro-max/data/typography.csv +58 -0
  22. package/.agent/.shared/ui-ux-pro-max/data/ui-reasoning.csv +101 -0
  23. package/.agent/.shared/ui-ux-pro-max/data/ux-guidelines.csv +100 -0
  24. package/.agent/.shared/ui-ux-pro-max/data/web-interface.csv +31 -0
  25. package/.agent/.shared/ui-ux-pro-max/scripts/__pycache__/core.cpython-313.pyc +0 -0
  26. package/.agent/.shared/ui-ux-pro-max/scripts/__pycache__/design_system.cpython-313.pyc +0 -0
  27. package/.agent/.shared/ui-ux-pro-max/scripts/core.py +258 -0
  28. package/.agent/.shared/ui-ux-pro-max/scripts/design_system.py +1067 -0
  29. package/.agent/.shared/ui-ux-pro-max/scripts/search.py +106 -0
  30. package/.agent/ARCHITECTURE.md +288 -0
  31. package/.agent/agents/ambient-agent.md +57 -0
  32. package/.agent/agents/debugger.md +55 -0
  33. package/.agent/agents/email-assistant.md +49 -0
  34. package/.agent/agents/file-manager.md +42 -0
  35. package/.agent/agents/python-developer.md +60 -0
  36. package/.agent/agents/scheduler.md +59 -0
  37. package/.agent/agents/web-developer.md +45 -0
  38. package/.agent/data/default.json +29 -0
  39. package/.agent/data/plugins-state.json +255 -0
  40. package/.agent/mcp_config.json +4 -0
  41. package/.agent/mcp_config_updated.json +12 -0
  42. package/.agent/plugins.json +5 -0
  43. package/.agent/rules/GEMINI.md +273 -0
  44. package/.agent/rules/allow-rule.md +77 -0
  45. package/.agent/rules/log-rule.md +83 -0
  46. package/.agent/rules/security-rule.md +93 -0
  47. package/.agent/scripts/auto_preview.py +148 -0
  48. package/.agent/scripts/checklist.py +217 -0
  49. package/.agent/scripts/session_manager.py +120 -0
  50. package/.agent/scripts/verify_all.py +327 -0
  51. package/.agent/skills/api-patterns/SKILL.md +81 -0
  52. package/.agent/skills/api-patterns/api-style.md +42 -0
  53. package/.agent/skills/api-patterns/auth.md +24 -0
  54. package/.agent/skills/api-patterns/documentation.md +26 -0
  55. package/.agent/skills/api-patterns/graphql.md +41 -0
  56. package/.agent/skills/api-patterns/rate-limiting.md +31 -0
  57. package/.agent/skills/api-patterns/response.md +37 -0
  58. package/.agent/skills/api-patterns/rest.md +40 -0
  59. package/.agent/skills/api-patterns/scripts/api_validator.py +211 -0
  60. package/.agent/skills/api-patterns/security-testing.md +122 -0
  61. package/.agent/skills/api-patterns/trpc.md +41 -0
  62. package/.agent/skills/api-patterns/versioning.md +22 -0
  63. package/.agent/skills/app-builder/SKILL.md +75 -0
  64. package/.agent/skills/app-builder/agent-coordination.md +71 -0
  65. package/.agent/skills/app-builder/feature-building.md +53 -0
  66. package/.agent/skills/app-builder/project-detection.md +34 -0
  67. package/.agent/skills/app-builder/scaffolding.md +118 -0
  68. package/.agent/skills/app-builder/tech-stack.md +40 -0
  69. package/.agent/skills/app-builder/templates/SKILL.md +39 -0
  70. package/.agent/skills/app-builder/templates/astro-static/TEMPLATE.md +76 -0
  71. package/.agent/skills/app-builder/templates/chrome-extension/TEMPLATE.md +92 -0
  72. package/.agent/skills/app-builder/templates/cli-tool/TEMPLATE.md +88 -0
  73. package/.agent/skills/app-builder/templates/electron-desktop/TEMPLATE.md +88 -0
  74. package/.agent/skills/app-builder/templates/express-api/TEMPLATE.md +83 -0
  75. package/.agent/skills/app-builder/templates/flutter-app/TEMPLATE.md +90 -0
  76. package/.agent/skills/app-builder/templates/monorepo-turborepo/TEMPLATE.md +90 -0
  77. package/.agent/skills/app-builder/templates/nextjs-fullstack/TEMPLATE.md +122 -0
  78. package/.agent/skills/app-builder/templates/nextjs-saas/TEMPLATE.md +122 -0
  79. package/.agent/skills/app-builder/templates/nextjs-static/TEMPLATE.md +169 -0
  80. package/.agent/skills/app-builder/templates/nuxt-app/TEMPLATE.md +134 -0
  81. package/.agent/skills/app-builder/templates/python-fastapi/TEMPLATE.md +83 -0
  82. package/.agent/skills/app-builder/templates/react-native-app/TEMPLATE.md +119 -0
  83. package/.agent/skills/architecture/SKILL.md +55 -0
  84. package/.agent/skills/architecture/context-discovery.md +43 -0
  85. package/.agent/skills/architecture/examples.md +94 -0
  86. package/.agent/skills/architecture/pattern-selection.md +68 -0
  87. package/.agent/skills/architecture/patterns-reference.md +50 -0
  88. package/.agent/skills/architecture/trade-off-analysis.md +77 -0
  89. package/.agent/skills/clean-code/SKILL.md +201 -0
  90. package/.agent/skills/doc.md +177 -0
  91. package/.agent/skills/frontend-design/SKILL.md +418 -0
  92. package/.agent/skills/frontend-design/animation-guide.md +331 -0
  93. package/.agent/skills/frontend-design/color-system.md +311 -0
  94. package/.agent/skills/frontend-design/decision-trees.md +418 -0
  95. package/.agent/skills/frontend-design/motion-graphics.md +306 -0
  96. package/.agent/skills/frontend-design/scripts/accessibility_checker.py +183 -0
  97. package/.agent/skills/frontend-design/scripts/ux_audit.py +722 -0
  98. package/.agent/skills/frontend-design/typography-system.md +345 -0
  99. package/.agent/skills/frontend-design/ux-psychology.md +1116 -0
  100. package/.agent/skills/frontend-design/visual-effects.md +383 -0
  101. package/.agent/skills/i18n-localization/SKILL.md +154 -0
  102. package/.agent/skills/i18n-localization/scripts/i18n_checker.py +241 -0
  103. package/.agent/skills/mcp-builder/SKILL.md +176 -0
  104. package/.agent/skills/web-design-guidelines/SKILL.md +57 -0
  105. package/.agent/workflows/brainstorm.md +113 -0
  106. package/.agent/workflows/create.md +59 -0
  107. package/.agent/workflows/debug.md +103 -0
  108. package/.agent/workflows/deploy.md +176 -0
  109. package/.agent/workflows/enhance.md +63 -0
  110. package/.agent/workflows/orchestrate.md +237 -0
  111. package/.agent/workflows/plan.md +89 -0
  112. package/.agent/workflows/preview.md +81 -0
  113. package/.agent/workflows/simple-test.md +42 -0
  114. package/.agent/workflows/status.md +86 -0
  115. package/.agent/workflows/structured-orchestrate.md +180 -0
  116. package/.agent/workflows/test.md +144 -0
  117. package/.agent/workflows/ui-ux-pro-max.md +296 -0
  118. package/.claude/settings.local.json +157 -149
  119. package/.editorconfig +56 -0
  120. package/.husky/pre-commit +4 -0
  121. package/.lintstagedrc +7 -0
  122. package/.prettierignore +29 -0
  123. package/.prettierrc +11 -0
  124. package/CLAUDE.md +2 -0
  125. package/README.md +64 -55
  126. package/SPEC.md +102 -61
  127. package/cli/bin/foliko.js +4 -4
  128. package/cli/src/commands/chat.js +53 -51
  129. package/cli/src/commands/list.js +40 -37
  130. package/cli/src/index.js +18 -18
  131. package/cli/src/ui/chat-ui.js +78 -76
  132. package/cli/src/utils/ansi.js +15 -15
  133. package/cli/src/utils/markdown.js +112 -116
  134. package/docker-compose.yml +1 -1
  135. package/docs/ai-sdk-optimization.md +655 -643
  136. package/docs/features.md +80 -80
  137. package/docs/quick-reference.md +49 -46
  138. package/docs/user-manual.md +411 -380
  139. package/examples/ambient-example.js +95 -97
  140. package/examples/basic.js +115 -110
  141. package/examples/bootstrap.js +52 -43
  142. package/examples/mcp-example.js +56 -53
  143. package/examples/skill-example.js +49 -49
  144. package/examples/test-chat.js +60 -58
  145. package/examples/test-mcp.js +49 -43
  146. package/examples/test-reload.js +38 -40
  147. package/examples/test-telegram.js +3 -3
  148. package/examples/test-tg-bot.js +7 -4
  149. package/examples/test-tg-simple.js +4 -3
  150. package/examples/test-tg.js +3 -3
  151. package/examples/test-think.js +13 -7
  152. package/examples/test-web-plugin.js +61 -56
  153. package/examples/test-weixin-feishu.js +40 -37
  154. package/examples/workflow.js +49 -49
  155. package/foliko-1.0.75.tgz +0 -0
  156. package/package.json +37 -3
  157. package/plugins/ai-plugin.js +7 -5
  158. package/plugins/ambient-agent/EventWatcher.js +113 -0
  159. package/plugins/ambient-agent/ExplorerLoop.js +640 -0
  160. package/plugins/ambient-agent/GoalManager.js +197 -0
  161. package/plugins/ambient-agent/Reflector.js +95 -0
  162. package/plugins/ambient-agent/StateStore.js +90 -0
  163. package/plugins/ambient-agent/constants.js +101 -0
  164. package/plugins/ambient-agent/index.js +579 -0
  165. package/plugins/default-plugins.js +62 -49
  166. package/plugins/email/constants.js +64 -0
  167. package/plugins/email/handlers.js +461 -0
  168. package/plugins/email/index.js +278 -0
  169. package/plugins/email/monitor.js +269 -0
  170. package/plugins/email/parser.js +138 -0
  171. package/plugins/email/reply.js +151 -0
  172. package/plugins/email/utils.js +124 -0
  173. package/plugins/feishu-plugin.js +23 -19
  174. package/plugins/file-system-plugin.js +456 -106
  175. package/plugins/install-plugin.js +6 -4
  176. package/plugins/python-executor-plugin.js +3 -1
  177. package/plugins/python-plugin-loader.js +10 -8
  178. package/plugins/rules-plugin.js +5 -3
  179. package/plugins/scheduler-plugin.js +18 -16
  180. package/plugins/session-plugin.js +3 -1
  181. package/plugins/storage-plugin.js +5 -3
  182. package/plugins/subagent-plugin.js +152 -92
  183. package/plugins/telegram-plugin.js +26 -19
  184. package/plugins/think-plugin.js +4 -2
  185. package/plugins/tools-plugin.js +3 -1
  186. package/plugins/web-plugin.js +15 -13
  187. package/plugins/weixin-plugin.js +43 -36
  188. package/reports/system-health-report-20260401.md +79 -0
  189. package/skills/ambient-agent/SKILL.md +49 -39
  190. package/skills/foliko-dev/AGENTS.md +64 -61
  191. package/skills/foliko-dev/SKILL.md +125 -119
  192. package/skills/mcp-usage/SKILL.md +19 -17
  193. package/skills/python-plugin-dev/SKILL.md +16 -15
  194. package/skills/skill-guide/SKILL.md +12 -12
  195. package/skills/subagent-guide/SKILL.md +237 -0
  196. package/skills/workflow-guide/SKILL.md +90 -45
  197. package/skills/workflow-troubleshooting/DEBUGGING.md +36 -21
  198. package/skills/workflow-troubleshooting/SKILL.md +156 -79
  199. package/src/capabilities/index.js +4 -4
  200. package/src/capabilities/skill-manager.js +211 -197
  201. package/src/capabilities/workflow-engine.js +461 -547
  202. package/src/core/agent-chat.js +426 -279
  203. package/src/core/agent.js +453 -249
  204. package/src/core/framework.js +183 -149
  205. package/src/core/index.js +8 -8
  206. package/src/core/plugin-base.js +52 -52
  207. package/src/core/plugin-manager.js +377 -281
  208. package/src/core/provider.js +35 -32
  209. package/src/core/sub-agent-config.js +264 -0
  210. package/src/core/system-prompt-builder.js +120 -0
  211. package/src/core/tool-registry.js +416 -33
  212. package/src/core/tool-router.js +149 -68
  213. package/src/executors/executor-base.js +58 -58
  214. package/src/executors/mcp-executor.js +269 -257
  215. package/src/index.js +5 -17
  216. package/src/utils/circuit-breaker.js +301 -0
  217. package/src/utils/error-boundary.js +363 -0
  218. package/src/utils/error.js +374 -0
  219. package/src/utils/event-emitter.js +20 -20
  220. package/src/utils/id.js +133 -0
  221. package/src/utils/index.js +217 -3
  222. package/src/utils/logger.js +181 -0
  223. package/src/utils/plugin-helpers.js +90 -0
  224. package/src/utils/retry.js +122 -0
  225. package/src/utils/sandbox.js +292 -0
  226. package/test/tool-registry-validation.test.js +218 -0
  227. package/test_report.md +70 -0
  228. package/website/docs/api.html +169 -107
  229. package/website/docs/configuration.html +296 -144
  230. package/website/docs/plugin-development.html +154 -85
  231. package/website/docs/project-structure.html +110 -109
  232. package/website/docs/skill-development.html +117 -61
  233. package/website/index.html +209 -205
  234. package/website/script.js +20 -17
  235. package/website/styles.css +1 -1
  236. package/plugins/ambient-agent-plugin.js +0 -1565
  237. package/plugins/email.js +0 -1142
@@ -1,207 +1,300 @@
1
1
  /**
2
2
  * PluginManager 插件管理器
3
3
  * 负责插件的加载、卸载、重载
4
+ *
5
+ * @module PluginManager
6
+ * @requires ./plugin-base
4
7
  */
5
8
 
6
- const { Plugin } = require('./plugin-base')
7
- const fs = require('fs')
8
- const path = require('path')
9
+ const { Plugin } = require('./plugin-base');
10
+ const { logger } = require('../utils/logger');
11
+ const { PluginError, PluginNotFoundError } = require('../utils/error');
12
+ const { safeJsonParse } = require('../utils');
13
+ const { resolvePluginPath, scanPluginNames } = require('../utils/plugin-helpers');
14
+ const fs = require('fs');
15
+ const path = require('path');
9
16
 
10
17
  class PluginManager {
11
18
  /**
12
19
  * @param {Framework} framework - 框架实例
13
20
  */
14
21
  constructor(framework) {
15
- this.framework = framework
16
- this._plugins = new Map()
17
- this._loading = false
18
- this._stateFile = path.join(process.cwd(), '.agent', 'data', 'plugins-state.json')
19
- this._knownPlugins = new Set() // 已知插件列表(包括未加载的)
22
+ this.framework = framework;
23
+ this._plugins = new Map();
24
+ this._loading = false;
25
+ this._stateFile = path.join(process.cwd(), '.agent', 'data', 'plugins-state.json');
26
+ this._knownPlugins = new Set(); // 已知插件列表(包括未加载的)
27
+
28
+ // 创建子日志器
29
+ this._log = logger.child('plugin-manager');
30
+
31
+ // 事件描述注册表(供 Ambient Agent 使用)
32
+ this._eventDescriptions = new Map();
33
+
34
+ // 状态缓存(加载后保持,避免重复读文件)
35
+ this._stateCache = null;
36
+
37
+ // 注册默认事件描述
38
+ this._registerDefaultEventDescriptions();
39
+ }
40
+
41
+ /**
42
+ * 注册默认事件描述
43
+ * @private
44
+ */
45
+ _registerDefaultEventDescriptions() {
46
+ // 这些事件由框架内置组件触发,无需注册
47
+ // 但为了文档完整性,提供默认描述
48
+ const defaultDescriptions = {
49
+ 'tool:result': {
50
+ description: '工具执行结果事件',
51
+ params: 'name(工具名), args(执行参数), result(执行结果), error(错误信息)',
52
+ source: 'framework',
53
+ },
54
+ 'tool:error': {
55
+ description: '工具执行错误事件',
56
+ params: 'name(工具名), args(执行参数), error(错误信息)',
57
+ source: 'framework',
58
+ },
59
+ 'agent:message': {
60
+ description: '代理消息事件',
61
+ params: 'content(消息内容), sessionId(会话ID)',
62
+ source: 'framework',
63
+ },
64
+ 'think:thought_completed': {
65
+ description: '思考完成事件',
66
+ params: 'mode(思考模式), topic(思考主题), thought(思考内容), depth(思考深度)',
67
+ source: 'think-plugin',
68
+ },
69
+ 'email:received': {
70
+ description: '收到邮件事件',
71
+ params:
72
+ 'from(发件人), to(收件人), subject(主题), text(正文), body(HTML正文), messageId(消息ID), timestamp(时间戳)',
73
+ source: 'email-plugin',
74
+ },
75
+ 'webhook:received': {
76
+ description: 'Webhook接收事件',
77
+ params: 'headers(HTTP头), body(请求体), query(查询参数), path(请求路径)',
78
+ source: 'web-plugin',
79
+ },
80
+ 'scheduler:reminder': {
81
+ description: '定时提醒事件',
82
+ params: 'taskId(任务ID), message(提醒消息), time(触发时间)',
83
+ source: 'scheduler-plugin',
84
+ },
85
+ 'scheduler:task_completed': {
86
+ description: '任务完成事件',
87
+ params: 'taskId(任务ID), result(任务结果)',
88
+ source: 'scheduler-plugin',
89
+ },
90
+ 'scheduler:task_failed': {
91
+ description: '任务失败事件',
92
+ params: 'taskId(任务ID), error(错误信息)',
93
+ source: 'scheduler-plugin',
94
+ },
95
+ };
96
+
97
+ for (const [eventType, info] of Object.entries(defaultDescriptions)) {
98
+ this._eventDescriptions.set(eventType, info);
99
+ }
20
100
  }
21
101
 
22
102
  /**
23
103
  * 获取状态文件路径
104
+ * @private
24
105
  */
25
106
  _getStateFile() {
26
- const dir = path.dirname(this._stateFile)
107
+ const dir = path.dirname(this._stateFile);
27
108
  if (!fs.existsSync(dir)) {
28
- fs.mkdirSync(dir, { recursive: true })
109
+ fs.mkdirSync(dir, { recursive: true });
29
110
  }
30
- return this._stateFile
111
+ return this._stateFile;
31
112
  }
32
113
 
33
114
  /**
34
115
  * 保存插件状态到文件
35
116
  * 注意:AI 插件配置不保存(从环境变量和命令行获取)
117
+ * @private
36
118
  */
37
119
  _saveState() {
38
120
  try {
39
- const stateFile = this._getStateFile()
121
+ const stateFile = this._getStateFile();
40
122
  // 读取现有状态(如果存在),保留原有内容
41
- let state = {}
42
- if (fs.existsSync(stateFile)) {
43
- try {
44
- state = JSON.parse(fs.readFileSync(stateFile, 'utf-8'))
45
- } catch (e) {
46
- // 文件损坏,使用空对象
47
- state = {}
48
- }
49
- }
123
+ let state = safeJsonParse(
124
+ fs.existsSync(stateFile) ? fs.readFileSync(stateFile, 'utf-8') : '{}',
125
+ {}
126
+ );
50
127
 
51
128
  // 合并新状态到现有状态
52
129
  for (const [name, entry] of this._plugins) {
53
130
  // AI 配置不保存,每次从环境变量和命令行获取
54
131
  if (name === 'ai') {
55
- state[name] = { enabled: entry.enabled }
132
+ state[name] = { enabled: entry.enabled };
56
133
  } else {
57
134
  state[name] = {
58
- ...(state[name] || {}), // 保留现有配置
135
+ ...(state[name] || {}), // 保留现有配置
59
136
  enabled: entry.enabled,
60
- config: entry.instance?.config || {}
61
- }
137
+ config: entry.instance?.config || {},
138
+ };
62
139
  }
63
140
  }
64
141
 
65
- fs.writeFileSync(stateFile, JSON.stringify(state, null, 2))
142
+ fs.writeFileSync(stateFile, JSON.stringify(state, null, 2));
143
+
144
+ // 更新缓存
145
+ this._stateCache = state;
66
146
  } catch (err) {
67
- console.error('[PluginManager] Failed to save state:', err.message)
147
+ this._log.error('Failed to save state:', err.message);
68
148
  }
69
149
  }
70
150
 
71
151
  /**
72
- * 加载插件状态从文件
152
+ * 加载插件状态从文件(带缓存)
153
+ * @private
154
+ * @returns {Object}
73
155
  */
74
156
  _loadState() {
157
+ // 返回缓存的状态
158
+ if (this._stateCache !== null) {
159
+ return this._stateCache;
160
+ }
161
+
75
162
  try {
76
- const stateFile = this._getStateFile()
163
+ const stateFile = this._getStateFile();
77
164
  if (fs.existsSync(stateFile)) {
78
- const state = JSON.parse(fs.readFileSync(stateFile, 'utf-8'))
79
- //console.log('[PluginManager] Loaded plugin state from file')
80
- return state
165
+ this._stateCache = safeJsonParse(fs.readFileSync(stateFile, 'utf-8'), {});
166
+ return this._stateCache;
81
167
  }
82
168
  } catch (err) {
83
- console.error('[PluginManager] Failed to load state:', err.message)
169
+ this._log.error('Failed to load state:', err.message);
84
170
  }
85
- return {}
171
+ this._stateCache = {};
172
+ return this._stateCache;
86
173
  }
87
174
 
88
175
  /**
89
176
  * 注册插件(不加载)
90
177
  * @param {Plugin|Object} plugin - 插件实例或定义
178
+ * @param {Object} [options] - 注册选项
179
+ * @returns {PluginManager}
180
+ * @throws {PluginError} 插件没有名称时抛出
91
181
  */
92
182
  register(plugin, options = {}) {
93
183
  if (!plugin.name) {
94
- throw new Error('Plugin must have a name')
184
+ throw new PluginError('Plugin must have a name');
95
185
  }
96
186
 
97
- let pluginInstance = plugin
187
+ let pluginInstance = plugin;
98
188
  if (!(plugin instanceof Plugin)) {
99
189
  // 从对象创建插件实例
100
- pluginInstance = this._createFromObject(plugin)
190
+ pluginInstance = this._createFromObject(plugin);
101
191
  }
102
192
 
103
193
  // 加载保存的状态
104
- const savedState = this._loadState()
105
- const savedEnabled = savedState[pluginInstance.name]?.enabled
106
- const savedConfig = savedState[pluginInstance.name]?.config
194
+ const savedState = this._loadState();
195
+ const savedEnabled = savedState[pluginInstance.name]?.enabled;
196
+ const savedConfig = savedState[pluginInstance.name]?.config;
107
197
 
108
198
  // 恢复保存的配置到插件实例(AI 插件不恢复配置,从环境变量获取)
109
199
  if (savedConfig && pluginInstance.config && pluginInstance.name !== 'ai') {
110
- pluginInstance.config = { ...pluginInstance.config, ...savedConfig }
200
+ pluginInstance.config = { ...pluginInstance.config, ...savedConfig };
111
201
  }
112
202
 
113
203
  // 系统插件强制启用,不能禁用
114
204
  // 普通插件:state 文件有记录则用 state,否则用插件默认配置
115
- let enabled
205
+ let enabled;
116
206
  if (pluginInstance.system) {
117
- enabled = true
207
+ enabled = true;
118
208
  } else if (savedEnabled !== undefined) {
119
- enabled = savedEnabled
209
+ enabled = savedEnabled;
120
210
  } else {
121
- enabled = pluginInstance.enabled !== undefined ? pluginInstance.enabled : true
211
+ enabled = pluginInstance.enabled !== undefined ? pluginInstance.enabled : true;
122
212
  }
123
213
 
124
214
  this._plugins.set(pluginInstance.name, {
125
215
  instance: pluginInstance,
126
216
  status: 'registered',
127
- enabled
128
- })
217
+ enabled,
218
+ });
129
219
 
130
- this.framework.emit('plugin:registered', pluginInstance)
131
- return this
220
+ this.framework.emit('plugin:registered', pluginInstance);
221
+ return this;
132
222
  }
133
223
 
134
224
  /**
135
225
  * 加载插件
136
226
  * @param {Plugin|Object} plugin - 插件实例或定义
137
- * @param {Object} options - 加载选项
138
- * @param {boolean} options.forceEnabled - 强制启用插件,忽略 state 文件的 disabled 状态(用于 .agent/plugins 目录的插件)
227
+ * @param {Object} [options] - 加载选项
228
+ * @param {boolean} [options.forceEnabled] - 强制启用插件,忽略 state 文件的 disabled 状态
229
+ * @returns {Promise<Plugin>}
230
+ * @throws {PluginError} 加载过程中发生错误时抛出
139
231
  */
140
232
  async load(plugin, options = {}) {
141
233
  if (this._loading) {
142
- throw new Error('Cannot load plugin during another load operation')
234
+ throw new PluginError('Cannot load plugin during another load operation');
143
235
  }
144
236
 
145
- this._loading = true
237
+ this._loading = true;
146
238
  try {
147
- let pluginInstance = plugin
239
+ let pluginInstance = plugin;
148
240
 
149
241
  if (!(plugin instanceof Plugin)) {
150
- pluginInstance = this._createFromObject(plugin)
242
+ pluginInstance = this._createFromObject(plugin);
151
243
  }
152
244
 
153
245
  // 如果已注册,使用已注册的实例和状态
154
- const existing = this._plugins.get(pluginInstance.name)
246
+ const existing = this._plugins.get(pluginInstance.name);
155
247
  if (existing) {
156
- pluginInstance = existing.instance
248
+ pluginInstance = existing.instance;
157
249
 
158
250
  // 如果插件被禁用,跳过加载
159
251
  if (!existing.enabled) {
160
- console.log(`[PluginManager] Plugin '${pluginInstance.name}' is disabled`)
161
- return pluginInstance
252
+ this._log.info(`Plugin '${pluginInstance.name}' is disabled`);
253
+ return pluginInstance;
162
254
  }
163
255
 
164
256
  // 如果已加载且已启动,直接返回
165
257
  if (existing.status === 'loaded' && pluginInstance._started) {
166
- console.warn(`[PluginManager] Plugin '${pluginInstance.name}' already loaded`)
167
- return pluginInstance
258
+ this._log.warn(`Plugin '${pluginInstance.name}' already loaded`);
259
+ return pluginInstance;
168
260
  }
169
261
 
170
262
  // 如果已加载但未启动(重载场景),直接启动
171
263
  if (existing.status === 'loaded' && !pluginInstance._started) {
172
264
  try {
173
265
  if (typeof pluginInstance.start === 'function') {
174
- await pluginInstance.start(this.framework)
175
- pluginInstance._started = true
266
+ await pluginInstance.start(this.framework);
267
+ pluginInstance._started = true;
176
268
  }
177
269
  } catch (err) {
178
- console.error(`[PluginManager] Start failed for '${pluginInstance.name}':`, err.message)
270
+ this._log.error(`Start failed for '${pluginInstance.name}':`, err.message);
179
271
  }
180
- this.framework.emit('plugin:loaded', pluginInstance)
181
- return pluginInstance
272
+ this.framework.emit('plugin:loaded', pluginInstance);
273
+ return pluginInstance;
182
274
  }
183
275
  } else {
184
276
  // 未注册,先注册
185
- this.register(pluginInstance, options)
277
+ this.register(pluginInstance, options);
186
278
  }
187
279
 
188
- const entry = this._plugins.get(pluginInstance.name)
280
+ const entry = this._plugins.get(pluginInstance.name);
189
281
 
190
282
  // 注册后再次检查 enabled 状态
191
283
  if (!entry.enabled) {
192
- //console.log(`[PluginManager] Plugin '${pluginInstance.name}' is disabled, skipping install`)
193
- return pluginInstance
284
+ return pluginInstance;
194
285
  }
195
286
 
196
287
  // 调用 install
197
288
  try {
198
- await entry.instance.install(this.framework)
289
+ await entry.instance.install(this.framework);
199
290
  } catch (err) {
200
- console.error(`[PluginManager] Install failed for '${pluginInstance.name}':`, err.message)
201
- throw err
291
+ this._log.error(`Install failed for '${pluginInstance.name}':`, err.message);
292
+ throw new PluginError(`Install failed for '${pluginInstance.name}'`, {
293
+ context: { originalError: err.message },
294
+ });
202
295
  }
203
296
 
204
- entry.status = 'loaded'
297
+ entry.status = 'loaded';
205
298
 
206
299
  // 如果处于 bootstrap 模式,由外部统一调用 startAll()
207
300
  // 否则直接启动
@@ -211,94 +304,101 @@ class PluginManager {
211
304
  // 非 bootstrap 模式下直接启动
212
305
  try {
213
306
  if (typeof pluginInstance.start === 'function') {
214
- await pluginInstance.start(this.framework)
215
- pluginInstance._started = true
307
+ await pluginInstance.start(this.framework);
308
+ pluginInstance._started = true;
216
309
  }
217
310
  } catch (err) {
218
- console.error(`[PluginManager] Start failed for '${pluginInstance.name}':`, err.message)
311
+ this._log.error(`Start failed for '${pluginInstance.name}':`, err.message);
219
312
  }
220
313
  }
221
314
 
222
- this.framework.emit('plugin:loaded', pluginInstance)
315
+ this.framework.emit('plugin:loaded', pluginInstance);
223
316
 
224
317
  // 保存状态(创建或更新 state 文件)
225
- this._saveState()
318
+ this._saveState();
226
319
 
227
- return pluginInstance
320
+ return pluginInstance;
228
321
  } finally {
229
- this._loading = false
322
+ this._loading = false;
230
323
  }
231
324
  }
232
325
 
233
326
  /**
234
327
  * 设置 bootstrap 模式
328
+ * @param {boolean} value
235
329
  */
236
330
  setBootstrapping(value) {
237
- this._bootstrapping = value
331
+ this._bootstrapping = value;
238
332
  }
239
333
 
240
334
  /**
241
335
  * 卸载插件
242
336
  * @param {string} name - 插件名称
337
+ * @returns {Promise<boolean>}
243
338
  */
244
339
  async unload(name) {
245
- const entry = this._plugins.get(name)
340
+ const entry = this._plugins.get(name);
246
341
  if (!entry) {
247
- return false
342
+ return false;
248
343
  }
249
344
 
250
- const { instance } = entry
345
+ const { instance } = entry;
251
346
 
252
347
  // 调用 uninstall
253
348
  try {
254
- await instance.uninstall(this.framework)
349
+ await instance.uninstall(this.framework);
255
350
  } catch (err) {
256
- console.error(`[PluginManager] Uninstall error for '${name}':`, err.message)
351
+ this._log.error(`Uninstall error for '${name}':`, err.message);
257
352
  }
258
353
 
259
- entry.status = 'unloaded'
260
- this.framework.emit('plugin:unloaded', instance)
261
- return true
354
+ entry.status = 'unloaded';
355
+ this.framework.emit('plugin:unloaded', instance);
356
+ return true;
262
357
  }
263
358
 
264
359
  /**
265
360
  * 重载插件
266
361
  * @param {string} name - 插件名称
362
+ * @returns {Promise<Plugin>}
363
+ * @throws {PluginNotFoundError} 插件不存在时抛出
267
364
  */
268
365
  async reload(name) {
269
- const entry = this._plugins.get(name)
366
+ const entry = this._plugins.get(name);
270
367
  if (!entry) {
271
- throw new Error(`Plugin '${name}' not found`)
368
+ throw new PluginNotFoundError(name);
272
369
  }
273
370
 
274
- const { instance } = entry
371
+ const { instance } = entry;
275
372
 
276
373
  // 调用 reload
277
374
  try {
278
- await instance.reload(this.framework)
279
- this.framework.emit('plugin:reloaded', instance)
375
+ await instance.reload(this.framework);
376
+ this.framework.emit('plugin:reloaded', instance);
280
377
  } catch (err) {
281
- console.error(`[PluginManager] Reload error for '${name}':`, err.message)
282
- throw err
378
+ this._log.error(`Reload error for '${name}':`, err.message);
379
+ throw new PluginError(`Reload error for '${name}'`, {
380
+ context: { originalError: err.message },
381
+ });
283
382
  }
284
383
 
285
- return instance
384
+ return instance;
286
385
  }
287
386
 
288
387
  /**
289
388
  * 重载所有插件
389
+ * @returns {Promise<void>}
290
390
  */
291
391
  async reloadAll() {
292
392
  // 1. 重置所有插件的启动标志
293
393
  for (const entry of this._plugins.values()) {
294
- entry.instance._started = false
394
+ entry.instance._started = false;
295
395
  }
296
396
 
297
397
  // 2. 扫描 .agent/plugins 目录,加载新插件
298
- await this._discoverCustomPlugins()
398
+ await this._discoverCustomPlugins();
299
399
 
300
400
  // 3. 启动所有未启动的插件
301
- await this.startAll()
401
+ await this.startAll();
302
402
  }
303
403
 
304
404
  /**
@@ -312,39 +412,7 @@ class PluginManager {
312
412
  * @private
313
413
  */
314
414
  _resolvePluginPath(pluginsDir, name) {
315
- const folderPath = path.join(pluginsDir, name)
316
- const filePath = path.join(pluginsDir, `${name}.js`)
317
-
318
- // 文件夹优先
319
- if (fs.existsSync(folderPath) && fs.statSync(folderPath).isDirectory()) {
320
- const pkgPath = path.join(folderPath, 'package.json')
321
- if (fs.existsSync(pkgPath)) {
322
- try {
323
- const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'))
324
- const main = pkg.main || 'index.js'
325
- const mainPath = path.join(folderPath, main)
326
- if (fs.existsSync(mainPath)) {
327
- return { path: mainPath, type: 'folder' }
328
- }
329
- } catch (err) {
330
- console.warn(`[_resolvePluginPath] Failed to parse package.json for ${name}:`, err.message)
331
- }
332
- }
333
- // 默认加载 index.js
334
- const indexPath = path.join(folderPath, 'index.js')
335
- if (fs.existsSync(indexPath)) {
336
- return { path: indexPath, type: 'folder' }
337
- }
338
- console.warn(`[_resolvePluginPath] No entry point found for plugin folder: ${name}`)
339
- return null
340
- }
341
-
342
- // 单文件回退
343
- if (fs.existsSync(filePath)) {
344
- return { path: filePath, type: 'file' }
345
- }
346
-
347
- return null
415
+ return resolvePluginPath(pluginsDir, name, { logger: this._log });
348
416
  }
349
417
 
350
418
  /**
@@ -354,27 +422,7 @@ class PluginManager {
354
422
  * @private
355
423
  */
356
424
  _scanPluginNames(pluginsDir) {
357
- if (!fs.existsSync(pluginsDir)) {
358
- return []
359
- }
360
-
361
- const names = new Set()
362
- const entries = fs.readdirSync(pluginsDir, { withFileTypes: true })
363
-
364
- for (const entry of entries) {
365
- if (entry.isDirectory()) {
366
- // 文件夹插件
367
- names.add(entry.name)
368
- } else if (entry.isFile() && entry.name.endsWith('.js')) {
369
- // 单文件插件(排除与文件夹同名的)
370
- const baseName = entry.name.replace(/\.js$/, '')
371
- if (!names.has(baseName)) {
372
- names.add(baseName)
373
- }
374
- }
375
- }
376
-
377
- return Array.from(names)
425
+ return scanPluginNames(pluginsDir);
378
426
  }
379
427
 
380
428
  /**
@@ -382,72 +430,76 @@ class PluginManager {
382
430
  * @private
383
431
  */
384
432
  async _discoverCustomPlugins() {
385
- const pluginsDir = path.resolve(process.cwd(), '.agent', 'plugins')
433
+ const pluginsDir = path.resolve(process.cwd(), '.agent', 'plugins');
386
434
  if (!fs.existsSync(pluginsDir)) {
387
- return
435
+ return;
388
436
  }
389
437
 
390
438
  // 扫描所有插件名称(支持文件夹和单文件)
391
- const pluginNames = this._scanPluginNames(pluginsDir)
439
+ const pluginNames = this._scanPluginNames(pluginsDir);
392
440
 
393
441
  // 从 pluginsDir 推导 agentDir(pluginsDir = <agentDir>/plugins)
394
- const agentDir = path.dirname(pluginsDir)
395
- const agentNodeModules = path.join(agentDir, 'node_modules')
442
+ const agentDir = path.dirname(pluginsDir);
443
+ const agentNodeModules = path.join(agentDir, 'node_modules');
396
444
 
397
445
  for (const pluginName of pluginNames) {
398
446
  try {
399
- const resolved = this._resolvePluginPath(pluginsDir, pluginName)
447
+ const resolved = this._resolvePluginPath(pluginsDir, pluginName);
400
448
  if (!resolved) {
401
- console.warn(`[PluginManager] Cannot resolve plugin: ${pluginName}`)
402
- continue
449
+ this._log.warn(`Cannot resolve plugin: ${pluginName}`);
450
+ continue;
403
451
  }
404
452
 
405
- const { path: pluginPath, type } = resolved
453
+ const { path: pluginPath, type } = resolved;
406
454
 
407
455
  // 添加模块路径到搜索路径(优先级从高到低)
408
456
  const modulePathsToAdd = [
409
- agentNodeModules, // .agent/node_modules(项目本地安装的包)
410
- path.join(__dirname, '..', '..', 'node_modules') // 全局安装的包
411
- ]
457
+ agentNodeModules, // .agent/node_modules(项目本地安装的包)
458
+ path.join(__dirname, '..', '..', 'node_modules'), // 全局安装的包
459
+ ];
412
460
  for (const mp of modulePathsToAdd) {
413
461
  if (fs.existsSync(mp) && !module.paths.includes(mp)) {
414
- module.paths.unshift(mp)
462
+ module.paths.unshift(mp);
415
463
  }
416
464
  }
417
465
 
418
466
  // 清除缓存
419
- delete require.cache[require.resolve(pluginPath)]
420
- const pluginModule = require(pluginPath)
467
+ delete require.cache[require.resolve(pluginPath)];
468
+ const pluginModule = require(pluginPath);
421
469
 
422
- let plugin
470
+ let plugin;
423
471
  if (typeof pluginModule === 'function') {
424
- plugin = pluginModule
472
+ plugin = pluginModule;
425
473
  } else if (pluginModule.default) {
426
- plugin = pluginModule.default
474
+ plugin = pluginModule.default;
427
475
  } else {
428
- plugin = pluginModule
476
+ plugin = pluginModule;
429
477
  }
430
478
 
431
479
  // 获取插件名称
432
- let resolvedPluginName
480
+ let resolvedPluginName;
433
481
  try {
434
- const tempPlugin = plugin.prototype instanceof require('./plugin-base')
435
- ? new plugin() : (typeof plugin === 'function' ? plugin() : plugin)
436
- resolvedPluginName = tempPlugin.name || pluginName
482
+ const tempPlugin =
483
+ plugin.prototype instanceof require('./plugin-base')
484
+ ? new plugin()
485
+ : typeof plugin === 'function'
486
+ ? plugin()
487
+ : plugin;
488
+ resolvedPluginName = tempPlugin.name || pluginName;
437
489
  } catch {
438
- resolvedPluginName = pluginName
490
+ resolvedPluginName = pluginName;
439
491
  }
440
492
 
441
493
  // 如果插件已加载且已启动,跳过
442
494
  if (this.has(resolvedPluginName) && this.get(resolvedPluginName)?._started) {
443
- continue
495
+ continue;
444
496
  }
445
497
 
446
- console.log(`[PluginManager] Loading new plugin: ${pluginName} (${type})`)
498
+ this._log.info(`Loading new plugin: ${pluginName} (${type})`);
447
499
  // .agent/plugins 目录下的插件默认强制启用,不受 state 文件影响
448
- await this.load(plugin, { forceEnabled: true })
500
+ await this.load(plugin, { forceEnabled: true });
449
501
  } catch (err) {
450
- console.error(`[PluginManager] Failed to load plugin ${pluginName}:`, err.message)
502
+ this._log.error(`Failed to load plugin ${pluginName}:`, err.message);
451
503
  }
452
504
  }
453
505
  }
@@ -455,117 +507,125 @@ class PluginManager {
455
507
  /**
456
508
  * 获取插件
457
509
  * @param {string} name - 插件名称
510
+ * @returns {Plugin|undefined}
458
511
  */
459
512
  get(name) {
460
- return this._plugins.get(name)?.instance
513
+ return this._plugins.get(name)?.instance;
461
514
  }
462
515
 
463
516
  /**
464
517
  * 获取所有已加载且已启用的插件
518
+ * @returns {Array<{name: string, instance: Plugin}>}
465
519
  */
466
520
  getAll() {
467
521
  return Array.from(this._plugins.values())
468
- .filter(e => e.status === 'loaded' && e.enabled)
469
- .map(e => ({ name: e.instance.name, instance: e.instance }))
522
+ .filter((e) => e.status === 'loaded' && e.enabled)
523
+ .map((e) => ({ name: e.instance.name, instance: e.instance }));
470
524
  }
471
525
 
472
526
  /**
473
527
  * 注册一个已知插件(但不加载)
474
528
  * 用于显示所有可用插件列表
475
529
  * @param {string} name - 插件名称
476
- * @param {Object} info - 插件信息
530
+ * @param {Object} [info] - 插件信息
477
531
  */
478
532
  registerKnownPlugin(name, info = {}) {
479
- this._knownPlugins.add(name)
533
+ this._knownPlugins.add(name);
480
534
  // 如果插件还没注册过,记录它的信息
481
535
  if (!this._plugins.has(name)) {
482
536
  this._plugins.set(name, {
483
537
  instance: { name, ...info },
484
538
  status: 'known',
485
- enabled: info.enabled !== undefined ? info.enabled : false
486
- })
539
+ enabled: info.enabled !== undefined ? info.enabled : false,
540
+ });
487
541
  }
488
542
  }
489
543
 
490
544
  /**
491
545
  * 获取所有已知插件(包括未加载的)
492
- * @returns {Array} 插件列表,包含 name, status, enabled
546
+ * @returns {Array<{name: string, status: string, enabled: boolean, version?: string, system?: boolean}>}
493
547
  */
494
548
  getAllKnown() {
495
- const result = []
549
+ const result = [];
496
550
  for (const name of this._knownPlugins) {
497
- const entry = this._plugins.get(name)
551
+ const entry = this._plugins.get(name);
498
552
  result.push({
499
553
  name,
500
554
  status: entry?.status || 'unknown',
501
555
  enabled: entry?.enabled || false,
502
556
  version: entry?.instance?.version,
503
- system: entry?.instance?.system || false
504
- })
557
+ system: entry?.instance?.system || false,
558
+ });
505
559
  }
506
560
  // 也加入已加载但不在 knownPlugins 中的
507
561
  for (const [name, entry] of this._plugins) {
508
- if (!result.find(p => p.name === name)) {
562
+ if (!result.find((p) => p.name === name)) {
509
563
  result.push({
510
564
  name,
511
565
  status: entry.status,
512
566
  enabled: entry.enabled,
513
567
  version: entry.instance?.version,
514
- system: entry.instance?.system || false
515
- })
568
+ system: entry.instance?.system || false,
569
+ });
516
570
  }
517
571
  }
518
- return result
572
+ return result;
519
573
  }
520
574
 
521
575
  /**
522
576
  * 检查插件是否存在
523
577
  * @param {string} name - 插件名称
578
+ * @returns {boolean}
524
579
  */
525
580
  has(name) {
526
- return this._plugins.has(name) || this._knownPlugins.has(name)
581
+ return this._plugins.has(name) || this._knownPlugins.has(name);
527
582
  }
528
583
 
529
584
  /**
530
585
  * 检查插件是否已加载
531
586
  * @param {string} name - 插件名称
587
+ * @returns {boolean}
532
588
  */
533
589
  isLoaded(name) {
534
- return this._plugins.get(name)?.status === 'loaded'
590
+ return this._plugins.get(name)?.status === 'loaded';
535
591
  }
536
592
 
537
593
  /**
538
594
  * 检查插件是否启用
539
595
  * @param {string} name - 插件名称
596
+ * @returns {boolean}
540
597
  */
541
598
  isEnabled(name) {
542
- return this._plugins.get(name)?.enabled === true
599
+ return this._plugins.get(name)?.enabled === true;
543
600
  }
544
601
 
545
602
  /**
546
603
  * 启用插件
547
604
  * @param {string} name - 插件名称
605
+ * @returns {Promise<void>}
606
+ * @throws {PluginNotFoundError} 插件不存在时抛出
607
+ * @throws {PluginError} 系统插件不能被禁用
548
608
  */
549
609
  async enable(name) {
550
- const entry = this._plugins.get(name)
610
+ const entry = this._plugins.get(name);
551
611
  if (!entry) {
552
- throw new Error(`Plugin '${name}' not found`)
612
+ throw new PluginNotFoundError(name);
553
613
  }
554
614
 
555
615
  // 系统插件不能被禁用,所以启用没有意义
556
616
  if (entry.instance?.system) {
557
- throw new Error(`Plugin '${name}' is a system plugin, cannot be disabled`)
617
+ throw new PluginError(`Plugin '${name}' is a system plugin, cannot be disabled`);
558
618
  }
559
619
 
560
620
  if (entry.enabled) {
561
- console.log(`[PluginManager] Plugin '${name}' already enabled`)
562
- return
621
+ this._log.info(`Plugin '${name}' already enabled`);
622
+ return;
563
623
  }
564
624
 
565
- entry.enabled = true
625
+ entry.enabled = true;
566
626
  // 同步更新插件实例的 enabled 属性,避免 load() 时被跳过
567
627
  if (entry.instance) {
568
- entry.instance.enabled = true
628
+ entry.instance.enabled = true;
569
629
  }
570
630
 
571
631
  // 如果插件已加载,尝试重新启动
@@ -574,54 +634,57 @@ class PluginManager {
574
634
  // 如果之前已经启动过,先调用 stop 停止旧实例
575
635
  if (entry.instance._started) {
576
636
  if (typeof entry.instance.stop === 'function') {
577
- await entry.instance.stop()
637
+ await entry.instance.stop();
578
638
  } else if (typeof entry.instance.stopBot === 'function') {
579
- await entry.instance.stopBot()
639
+ await entry.instance.stopBot();
580
640
  }
581
641
  }
582
642
  // 调用 reload 让插件重新初始化(会调用 install 和 start)
583
- await this.reload(name)
643
+ await this.reload(name);
584
644
  } catch (err) {
585
- console.error(`[PluginManager] Enable/reload failed for '${name}':`, err.message)
645
+ this._log.error(`Enable/reload failed for '${name}':`, err.message);
586
646
  }
587
647
  } else if (entry.status === 'registered' || entry.status === 'known') {
588
648
  // 插件只注册过但未加载,现在加载它
589
649
  try {
590
- await this.load(entry.instance)
650
+ await this.load(entry.instance);
591
651
  } catch (err) {
592
- console.error(`[PluginManager] Enable/load failed for '${name}':`, err.message)
652
+ this._log.error(`Enable/load failed for '${name}':`, err.message);
593
653
  }
594
654
  }
595
655
 
596
- this.framework.emit('plugin:enabled', entry.instance)
597
- this._saveState()
598
- console.log(`[PluginManager] Plugin '${name}' enabled`)
656
+ this.framework.emit('plugin:enabled', entry.instance);
657
+ this._saveState();
658
+ this._log.info(`Plugin '${name}' enabled`);
599
659
  }
600
660
 
601
661
  /**
602
662
  * 禁用插件
603
663
  * @param {string} name - 插件名称
664
+ * @returns {Promise<void>}
665
+ * @throws {PluginNotFoundError} 插件不存在时抛出
666
+ * @throws {PluginError} 系统插件不能被禁用
604
667
  */
605
668
  async disable(name) {
606
- const entry = this._plugins.get(name)
669
+ const entry = this._plugins.get(name);
607
670
  if (!entry) {
608
- throw new Error(`Plugin '${name}' not found`)
671
+ throw new PluginNotFoundError(name);
609
672
  }
610
673
 
611
674
  // 系统插件不能被禁用
612
675
  if (entry.instance?.system) {
613
- throw new Error(`Plugin '${name}' is a system plugin, cannot be disabled`)
676
+ throw new PluginError(`Plugin '${name}' is a system plugin, cannot be disabled`);
614
677
  }
615
678
 
616
679
  if (!entry.enabled) {
617
- console.log(`[PluginManager] Plugin '${name}' already disabled`)
618
- return
680
+ this._log.info(`Plugin '${name}' already disabled`);
681
+ return;
619
682
  }
620
683
 
621
- entry.enabled = false
684
+ entry.enabled = false;
622
685
  // 同步更新插件实例的 enabled 属性
623
686
  if (entry.instance) {
624
- entry.instance.enabled = false
687
+ entry.instance.enabled = false;
625
688
  }
626
689
 
627
690
  // 如果插件正在运行,停止它
@@ -629,86 +692,70 @@ class PluginManager {
629
692
  try {
630
693
  // 优先调用 stop 方法,其次调用 stopBot 方法
631
694
  if (typeof entry.instance.stop === 'function') {
632
- await entry.instance.stop()
695
+ await entry.instance.stop();
633
696
  } else if (typeof entry.instance.stopBot === 'function') {
634
- await entry.instance.stopBot()
697
+ await entry.instance.stopBot();
635
698
  }
636
- entry.instance._started = false
699
+ entry.instance._started = false;
637
700
  } catch (err) {
638
- console.error(`[PluginManager] Stop failed for '${name}':`, err.message)
701
+ this._log.error(`Stop failed for '${name}':`, err.message);
639
702
  }
640
703
  }
641
704
 
642
- this.framework.emit('plugin:disabled', entry.instance)
643
- this._saveState()
644
- console.log(`[PluginManager] Plugin '${name}' disabled`)
705
+ this.framework.emit('plugin:disabled', entry.instance);
706
+ this._saveState();
707
+ this._log.info(`Plugin '${name}' disabled`);
645
708
  }
646
709
 
647
710
  /**
648
711
  * 更新插件配置
649
712
  * @param {string} name - 插件名称
650
713
  * @param {Object} config - 新配置(会合并到现有配置)
714
+ * @returns {Object} 更新后的配置
715
+ * @throws {PluginNotFoundError} 插件不存在时抛出
651
716
  */
652
717
  updatePluginConfig(name, config) {
653
- const entry = this._plugins.get(name)
718
+ const entry = this._plugins.get(name);
654
719
  if (!entry) {
655
- throw new Error(`Plugin '${name}' not found`)
720
+ throw new PluginNotFoundError(name);
656
721
  }
657
722
 
658
723
  if (!entry.instance.config) {
659
- entry.instance.config = {}
724
+ entry.instance.config = {};
660
725
  }
661
726
 
662
727
  // 合并配置
663
- entry.instance.config = { ...entry.instance.config, ...config }
728
+ entry.instance.config = { ...entry.instance.config, ...config };
664
729
 
665
730
  // 保存状态
666
- this._saveState()
667
- console.log(`[PluginManager] Plugin '${name}' config updated`)
731
+ this._saveState();
732
+ this._log.info(`Plugin '${name}' config updated`);
668
733
 
669
- return entry.instance.config
734
+ return entry.instance.config;
670
735
  }
671
736
 
672
737
  /**
673
738
  * 启动所有已加载但未启动的插件(按优先级排序)
739
+ * @returns {Promise<void>}
674
740
  */
675
741
  async startAll() {
676
742
  const entries = Array.from(this._plugins.values())
677
- .filter(e => e.status === 'loaded' && e.enabled) // 只启动已启用且已加载的插件
678
- .sort((a, b) => (a.instance.priority || 100) - (b.instance.priority || 100))
743
+ .filter((e) => e.status === 'loaded' && e.enabled) // 只启动已启用且已加载的插件
744
+ .sort((a, b) => (a.instance.priority || 100) - (b.instance.priority || 100));
679
745
 
680
746
  for (const entry of entries) {
681
- const instance = entry.instance
747
+ const instance = entry.instance;
682
748
  // 跳过已经启动过的插件
683
749
  if (instance._started) {
684
- continue
685
- }
686
- try {
687
- if (typeof instance.start === 'function') {
688
- await instance.start(this.framework)
689
- instance._started = true
690
- }
691
- } catch (err) {
692
- console.error(`[PluginManager] Start failed for '${instance.name}':`, err.message)
750
+ continue;
693
751
  }
694
- }
695
- }
696
-
697
- /**
698
- * 启动所有已加载插件
699
- * @private
700
- */
701
- async _startAll() {
702
- const loaded = this.getAll()
703
- .sort((a, b) => (a.instance.priority || 100) - (b.instance.priority || 100))
704
-
705
- for (const { instance } of loaded) {
706
752
  try {
707
753
  if (typeof instance.start === 'function') {
708
- await instance.start(this.framework)
754
+ await instance.start(this.framework);
755
+ instance._started = true;
709
756
  }
710
757
  } catch (err) {
711
- console.error(`[PluginManager] Start failed for '${instance.name}':`, err.message)
758
+ this._log.error(`Start failed for '${instance.name}':`, err.message);
712
759
  }
713
760
  }
714
761
  }
@@ -716,52 +763,101 @@ class PluginManager {
716
763
  /**
717
764
  * 从对象创建插件实例
718
765
  * @private
766
+ * @param {Object|Function} obj
767
+ * @returns {Plugin}
719
768
  */
720
769
  _createFromObject(obj) {
721
- const { Plugin } = require('./plugin-base')
770
+ const { Plugin } = require('./plugin-base');
722
771
 
723
772
  // 如果是类(构造函数),直接实例化
724
773
  if (typeof obj === 'function') {
725
774
  // 检查是否是 Plugin 的子类(通过 prototype chain)
726
775
  if (obj.prototype instanceof Plugin) {
727
- return new obj()
776
+ return new obj();
728
777
  }
729
778
  // 否则是工厂函数,调用它获取类或实例
730
- const result = obj(Plugin)
779
+ const result = obj(Plugin);
731
780
  // 递归处理返回值
732
781
  if (typeof result === 'function' && result.prototype instanceof Plugin) {
733
- return new result()
782
+ return new result();
734
783
  }
735
- return result
784
+ return result;
736
785
  }
737
786
 
738
787
  // 支持对象形式: { name, version, install, start, ... }
739
788
  class AnonymousPlugin extends Plugin {
740
789
  constructor() {
741
- super()
742
- this.name = obj.name
743
- this.version = obj.version || '1.0.0'
744
- this.description = obj.description || ''
745
- this.priority = obj.priority || 100
790
+ super();
791
+ this.name = obj.name;
792
+ this.version = obj.version || '1.0.0';
793
+ this.description = obj.description || '';
794
+ this.priority = obj.priority || 100;
746
795
 
747
796
  // 如果提供了 install/start/reload/uninstall 方法,绑定它们
748
797
  if (typeof obj.install === 'function') {
749
- this.install = obj.install.bind(this)
798
+ this.install = obj.install.bind(this);
750
799
  }
751
800
  if (typeof obj.start === 'function') {
752
- this.start = obj.start.bind(this)
801
+ this.start = obj.start.bind(this);
753
802
  }
754
803
  if (typeof obj.reload === 'function') {
755
- this.reload = obj.reload.bind(this)
804
+ this.reload = obj.reload.bind(this);
756
805
  }
757
806
  if (typeof obj.uninstall === 'function') {
758
- this.uninstall = obj.uninstall.bind(this)
807
+ this.uninstall = obj.uninstall.bind(this);
759
808
  }
760
809
  }
761
810
  }
762
811
 
763
- return new AnonymousPlugin()
812
+ return new AnonymousPlugin();
813
+ }
814
+
815
+ // ============================================================================
816
+ // 事件描述注册(供 Ambient Agent 使用)
817
+ // ============================================================================
818
+
819
+ /**
820
+ * 注册事件描述
821
+ * @param {string} eventType - 事件类型
822
+ * @param {string} description - 事件描述
823
+ * @param {Object} [schema] - 事件参数 Schema
824
+ */
825
+ registerEventDescription(eventType, description, schema = null) {
826
+ const existing = this._eventDescriptions.get(eventType);
827
+ this._eventDescriptions.set(eventType, {
828
+ description,
829
+ params: schema || existing?.params || '',
830
+ source: 'dynamic',
831
+ updatedAt: new Date().toISOString(),
832
+ });
833
+ return this;
834
+ }
835
+
836
+ /**
837
+ * 获取所有事件描述
838
+ * @returns {Map<string, Object>}
839
+ */
840
+ getEventDescriptions() {
841
+ return new Map(this._eventDescriptions);
842
+ }
843
+
844
+ /**
845
+ * 获取单个事件描述
846
+ * @param {string} eventType
847
+ * @returns {Object|null}
848
+ */
849
+ getEventDescription(eventType) {
850
+ return this._eventDescriptions.get(eventType) || null;
851
+ }
852
+
853
+ /**
854
+ * 检查事件是否已注册
855
+ * @param {string} eventType
856
+ * @returns {boolean}
857
+ */
858
+ hasEventDescription(eventType) {
859
+ return this._eventDescriptions.has(eventType);
764
860
  }
765
861
  }
766
862
 
767
- module.exports = { PluginManager }
863
+ module.exports = { PluginManager };