foliko 1.0.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.
Files changed (54) hide show
  1. package/.claude/settings.local.json +30 -0
  2. package/22.txt +10 -0
  3. package/README.md +218 -0
  4. package/SPEC.md +452 -0
  5. package/cli/bin/foliko.js +12 -0
  6. package/cli/src/commands/chat.js +75 -0
  7. package/cli/src/index.js +64 -0
  8. package/cli/src/ui/chat-ui.js +272 -0
  9. package/cli/src/utils/ansi.js +40 -0
  10. package/cli/src/utils/markdown.js +296 -0
  11. package/docs/quick-reference.md +131 -0
  12. package/docs/user-manual.md +1205 -0
  13. package/examples/basic.js +110 -0
  14. package/examples/bootstrap.js +93 -0
  15. package/examples/mcp-example.js +53 -0
  16. package/examples/skill-example.js +49 -0
  17. package/examples/workflow.js +158 -0
  18. package/package.json +36 -0
  19. package/plugins/ai-plugin.js +89 -0
  20. package/plugins/audit-plugin.js +187 -0
  21. package/plugins/default-plugins.js +412 -0
  22. package/plugins/file-system-plugin.js +344 -0
  23. package/plugins/install-plugin.js +93 -0
  24. package/plugins/python-executor-plugin.js +331 -0
  25. package/plugins/rules-plugin.js +292 -0
  26. package/plugins/scheduler-plugin.js +426 -0
  27. package/plugins/session-plugin.js +343 -0
  28. package/plugins/shell-executor-plugin.js +196 -0
  29. package/plugins/storage-plugin.js +237 -0
  30. package/plugins/subagent-plugin.js +395 -0
  31. package/plugins/think-plugin.js +329 -0
  32. package/plugins/tools-plugin.js +114 -0
  33. package/skills/mcp-usage/SKILL.md +198 -0
  34. package/skills/vb-agent-dev/AGENTS.md +162 -0
  35. package/skills/vb-agent-dev/SKILL.md +370 -0
  36. package/src/capabilities/index.js +11 -0
  37. package/src/capabilities/skill-manager.js +319 -0
  38. package/src/capabilities/workflow-engine.js +401 -0
  39. package/src/core/agent-chat.js +311 -0
  40. package/src/core/agent.js +573 -0
  41. package/src/core/framework.js +255 -0
  42. package/src/core/index.js +19 -0
  43. package/src/core/plugin-base.js +205 -0
  44. package/src/core/plugin-manager.js +392 -0
  45. package/src/core/provider.js +108 -0
  46. package/src/core/tool-registry.js +134 -0
  47. package/src/core/tool-router.js +216 -0
  48. package/src/executors/executor-base.js +58 -0
  49. package/src/executors/mcp-executor.js +728 -0
  50. package/src/index.js +37 -0
  51. package/src/utils/event-emitter.js +97 -0
  52. package/test-chat.js +129 -0
  53. package/test-mcp.js +79 -0
  54. package/test-reload.js +61 -0
@@ -0,0 +1,187 @@
1
+ /**
2
+ * Audit 审计日志插件
3
+ * 记录所有操作日志,支持日志查询和统计
4
+ */
5
+
6
+ const { Plugin } = require('../src/core/plugin-base')
7
+ const { z } = require('zod')
8
+
9
+ class AuditPlugin extends Plugin {
10
+ constructor(config = {}) {
11
+ super()
12
+ this.name = 'audit'
13
+ this.version = '1.0.0'
14
+ this.description = '审计日志插件,记录工具调用、技能执行、错误信息等操作历史'
15
+ this.priority = 20
16
+
17
+ this.config = {
18
+ maxLogs: config.maxLogs || 1000,
19
+ retentionDays: config.retentionDays || 30,
20
+ logDir: config.logDir || '.agent/logs'
21
+ }
22
+
23
+ this._framework = null
24
+ this._logs = []
25
+ this._logIdCounter = 0
26
+ }
27
+
28
+ install(framework) {
29
+ this._framework = framework
30
+
31
+ // 监听框架事件
32
+ framework.on('tool:call', (data) => this._log('tool_call', data))
33
+ framework.on('tool:result', (data) => this._log('tool_result', data))
34
+ framework.on('tool:error', (data) => this._log('tool_error', data))
35
+ framework.on('agent:message', (data) => this._log('agent_message', data))
36
+
37
+ return this
38
+ }
39
+
40
+ start(framework) {
41
+ // 注册审计查询工具
42
+ framework.registerTool({
43
+ name: 'audit_query',
44
+ description: '查询审计日志',
45
+ inputSchema: z.object({
46
+ type: z.string().optional().describe('日志类型: tool_call, tool_result, tool_error, agent_message'),
47
+ limit: z.number().optional().describe('返回数量限制,默认 50'),
48
+ offset: z.number().optional().describe('偏移量,默认 0')
49
+ }),
50
+ execute: async (args) => {
51
+ let logs = this._logs
52
+
53
+ if (args.type) {
54
+ logs = logs.filter(log => log.type === args.type)
55
+ }
56
+
57
+ const offset = args.offset || 0
58
+ const limit = args.limit || 50
59
+
60
+ return {
61
+ success: true,
62
+ logs: logs.slice(-limit - offset, logs.length - offset),
63
+ total: logs.length,
64
+ offset,
65
+ limit
66
+ }
67
+ }
68
+ })
69
+
70
+ framework.registerTool({
71
+ name: 'audit_stats',
72
+ description: '获取审计统计信息',
73
+ inputSchema: z.object({}),
74
+ execute: async () => {
75
+ const stats = this.getStats()
76
+ return {
77
+ success: true,
78
+ stats
79
+ }
80
+ }
81
+ })
82
+
83
+ framework.registerTool({
84
+ name: 'audit_export',
85
+ description: '导出审计日志',
86
+ inputSchema: z.object({
87
+ format: z.string().optional().describe('导出格式: json (默认), csv'),
88
+ type: z.string().optional().describe('日志类型过滤'),
89
+ startDate: z.string().optional().describe('开始日期 ISO 格式'),
90
+ endDate: z.string().optional().describe('结束日期 ISO 格式')
91
+ }),
92
+ execute: async (args) => {
93
+ const result = this.exportLogs(args)
94
+ return {
95
+ success: true,
96
+ count: result.logs.length,
97
+ data: result.logs
98
+ }
99
+ }
100
+ })
101
+
102
+ return this
103
+ }
104
+
105
+ /**
106
+ * 记录日志
107
+ * @private
108
+ */
109
+ _log(type, data) {
110
+ const log = {
111
+ id: ++this._logIdCounter,
112
+ type,
113
+ data,
114
+ timestamp: new Date()
115
+ }
116
+
117
+ this._logs.push(log)
118
+
119
+ // 限制日志数量
120
+ if (this._logs.length > this.config.maxLogs) {
121
+ this._logs = this._logs.slice(-this.config.maxLogs)
122
+ }
123
+
124
+ return log
125
+ }
126
+
127
+ /**
128
+ * 获取统计信息
129
+ */
130
+ getStats() {
131
+ const counts = {}
132
+ for (const log of this._logs) {
133
+ counts[log.type] = (counts[log.type] || 0) + 1
134
+ }
135
+
136
+ return {
137
+ total: this._logs.length,
138
+ maxLogs: this.config.maxLogs,
139
+ counts,
140
+ oldest: this._logs.length > 0 ? this._logs[0].timestamp : null,
141
+ newest: this._logs.length > 0 ? this._logs[this._logs.length - 1].timestamp : null
142
+ }
143
+ }
144
+
145
+ /**
146
+ * 导出日志
147
+ */
148
+ exportLogs(options = {}) {
149
+ let logs = [...this._logs]
150
+
151
+ // 按类型过滤
152
+ if (options.type) {
153
+ logs = logs.filter(log => log.type === options.type)
154
+ }
155
+
156
+ // 按日期过滤
157
+ if (options.startDate) {
158
+ const start = new Date(options.startDate)
159
+ logs = logs.filter(log => log.timestamp >= start)
160
+ }
161
+
162
+ if (options.endDate) {
163
+ const end = new Date(options.endDate)
164
+ logs = logs.filter(log => log.timestamp <= end)
165
+ }
166
+
167
+ return { logs }
168
+ }
169
+
170
+ /**
171
+ * 手动记录日志
172
+ */
173
+ log(type, data) {
174
+ return this._log(type, data)
175
+ }
176
+
177
+ reload(framework) {
178
+ this._framework = framework
179
+ }
180
+
181
+ uninstall(framework) {
182
+ this._logs = []
183
+ this._framework = null
184
+ }
185
+ }
186
+
187
+ module.exports = { AuditPlugin }
@@ -0,0 +1,412 @@
1
+ /**
2
+ * 默认插件配置加载器
3
+ * 检测 .agent/ 目录下的配置,提供给 bootstrap() 使用
4
+ */
5
+
6
+ const fs = require('fs')
7
+ const path = require('path')
8
+ const { Plugin } = require('../src/core/plugin-base')
9
+
10
+ /**
11
+ * 加载 .agent 目录下的配置
12
+ * 返回配置对象,可用于手动加载插件
13
+ */
14
+ function loadAgentConfig(agentDir = '.agent') {
15
+ const config = {
16
+ agentDir,
17
+ ai: {},
18
+ mcpServers: {},
19
+ plugins: [],
20
+ skillsDirs: []
21
+ }
22
+
23
+ const resolvedDir = path.resolve(process.cwd(), agentDir)
24
+
25
+ if (!fs.existsSync(resolvedDir)) {
26
+ console.log(`[AgentConfig] .agent directory not found: ${resolvedDir}`)
27
+ return config
28
+ }
29
+
30
+ console.log(`[AgentConfig] Loading config from: ${resolvedDir}`)
31
+
32
+ // 加载 config 文件(key=value 格式)
33
+ const configFile = path.join(resolvedDir, 'config')
34
+ if (fs.existsSync(configFile)) {
35
+ try {
36
+ const lines = fs.readFileSync(configFile, 'utf-8').split('\n')
37
+ for (const line of lines) {
38
+ const trimmed = line.trim()
39
+ if (!trimmed || trimmed.startsWith('#')) continue
40
+
41
+ const colonIndex = trimmed.indexOf(':')
42
+ if (colonIndex === -1) continue
43
+
44
+ const key = trimmed.substring(0, colonIndex).trim()
45
+ const value = trimmed.substring(colonIndex + 1).trim()
46
+
47
+ if (key === 'ai_key' || key === 'api_key') {
48
+ config.ai.apiKey = value
49
+ } else if (key === 'ai_model' || key === 'model') {
50
+ config.ai.model = value
51
+ } else if (key === 'ai_provider' || key === 'provider') {
52
+ config.ai.provider = value
53
+ } else if (key === 'ai_base_url' || key === 'baseURL') {
54
+ config.ai.baseURL = value
55
+ }
56
+ }
57
+ } catch (err) {
58
+ console.error(`[AgentConfig] Failed to load config:`, err.message)
59
+ }
60
+ }
61
+
62
+ // 加载 ai.json
63
+ const aiFile = path.join(resolvedDir, 'ai.json')
64
+ if (fs.existsSync(aiFile)) {
65
+ try {
66
+ const content = fs.readFileSync(aiFile, 'utf-8')
67
+ const aiConfig = JSON.parse(content)
68
+ config.ai = { ...config.ai, ...aiConfig }
69
+ } catch (err) {
70
+ console.error(`[AgentConfig] Failed to load ai.json:`, err.message)
71
+ }
72
+ }
73
+
74
+ // 加载 plugins.json
75
+ const pluginsFile = path.join(resolvedDir, 'plugins.json')
76
+ if (fs.existsSync(pluginsFile)) {
77
+ try {
78
+ const content = fs.readFileSync(pluginsFile, 'utf-8')
79
+ config.plugins = JSON.parse(content)
80
+ } catch (err) {
81
+ console.error(`[AgentConfig] Failed to load plugins.json:`, err.message)
82
+ }
83
+ }
84
+
85
+ // 加载 mcp_config.json
86
+ const mcpFile = path.join(resolvedDir, 'mcp_config.json')
87
+ if (fs.existsSync(mcpFile)) {
88
+ try {
89
+ const content = fs.readFileSync(mcpFile, 'utf-8')
90
+ const mcpConfig = JSON.parse(content)
91
+ config.mcpServers = mcpConfig.mcpServers || {}
92
+ } catch (err) {
93
+ console.error(`[AgentConfig] Failed to load mcp_config.json:`, err.message)
94
+ }
95
+ }
96
+
97
+ // 添加 .agent/skills 目录(不存在则创建)
98
+ const skillsDir = path.join(resolvedDir, 'skills')
99
+ if (fs.existsSync(resolvedDir) && !fs.existsSync(skillsDir)) {
100
+ fs.mkdirSync(skillsDir, { recursive: true })
101
+ console.log(`[AgentConfig] Created skills directory: ${skillsDir}`)
102
+ }
103
+ if (fs.existsSync(skillsDir)) {
104
+ config.skillsDirs.push(skillsDir)
105
+ }
106
+
107
+ // 添加 agentDir 父目录的 skills/ 目录
108
+ const parentDir = path.dirname(__dirname)
109
+ const rootSkillsDir = path.join(parentDir, 'skills')
110
+ if (fs.existsSync(rootSkillsDir)) {
111
+ config.skillsDirs.push(rootSkillsDir)
112
+ }
113
+
114
+ // const defaultSkillsDir = path.join(process.cwd(), 'defaultSkills')
115
+ // if (fs.existsSync(defaultSkillsDir)) {
116
+ // config.skillsDirs.push(defaultSkillsDir)
117
+ // }
118
+
119
+ return config
120
+ }
121
+
122
+ /**
123
+ * DefaultPlugins - 默认插件配置加载器
124
+ */
125
+ class DefaultPlugins extends Plugin {
126
+ constructor(config = {}) {
127
+ super()
128
+ this.name = 'defaults'
129
+ this.version = '1.0.0'
130
+ this.description = '默认插件配置加载器'
131
+ this.priority = 0 // 最先加载
132
+
133
+ this._framework = null
134
+ this._agentDir = config.agentDir || '.agent'
135
+ this._config = null
136
+ }
137
+
138
+ install(framework) {
139
+ this._framework = framework
140
+ return this
141
+ }
142
+
143
+ start(framework) {
144
+ // 加载配置
145
+ this._config = loadAgentConfig(this._agentDir)
146
+ return this
147
+ }
148
+
149
+ /**
150
+ * 获取加载的配置
151
+ */
152
+ getConfig() {
153
+ return this._config || loadAgentConfig(this._agentDir)
154
+ }
155
+
156
+ reload(framework) {
157
+ this._framework = framework
158
+ this._config = loadAgentConfig(this._agentDir)
159
+ }
160
+
161
+ uninstall(framework) {
162
+ this._framework = null
163
+ this._config = null
164
+ }
165
+ }
166
+
167
+ /**
168
+ * Bootstrap 辅助函数
169
+ * 根据配置加载所有默认插件
170
+ */
171
+ async function bootstrapDefaults(framework, config = {}) {
172
+ // 如果已经有配置,使用现有配置;否则加载
173
+ const agentConfig = config._config
174
+ if (!agentConfig) {
175
+ console.error('[Bootstrap] No config provided, skipping plugin loading')
176
+ return
177
+ }
178
+
179
+ // 设置 bootstrap 模式,避免重复启动
180
+ framework.pluginManager.setBootstrapping(true)
181
+
182
+ // 合并 AI 配置
183
+ const aiConfig = { ...agentConfig.ai, ...config.aiConfig }
184
+
185
+ // 0. Install 工具插件(最先加载,让其他插件能用到它的 node_modules)
186
+ if (!framework.pluginManager.has('install')) {
187
+ const { InstallPlugin } = require('./install-plugin')
188
+ await framework.loadPlugin(new InstallPlugin({ agentDir: agentConfig.agentDir }))
189
+ console.log('[Bootstrap] Install Plugin loaded')
190
+ }
191
+
192
+ // 合并 skillsDirs 配置(bootstrap 传入的优先)
193
+ const skillsDirs = [
194
+ ...(config.skillsDirs || []),
195
+ ...(agentConfig.skillsDirs || [])
196
+ ]
197
+
198
+ console.log('[Bootstrap] Loading default plugins...')
199
+
200
+ // 检查是否已经加载
201
+ if (framework.pluginManager.has('ai')) {
202
+ console.log('[Bootstrap] AI Plugin already loaded, skipping')
203
+ } else if (aiConfig.provider || aiConfig.model || aiConfig.apiKey) {
204
+ const { AIPlugin } = require('./ai-plugin')
205
+ const aiPlugin = new AIPlugin({
206
+ provider: aiConfig.provider || 'deepseek',
207
+ model: aiConfig.model || 'deepseek-chat',
208
+ apiKey: aiConfig.apiKey || process.env.DEEPSEEK_API_KEY,
209
+ baseURL: aiConfig.baseURL
210
+ })
211
+ await framework.loadPlugin(aiPlugin)
212
+ console.log('[Bootstrap] AI Plugin loaded')
213
+ }
214
+
215
+ // 2. Storage 存储插件
216
+ if (!framework.pluginManager.has('storage')) {
217
+ const { StoragePlugin } = require('./storage-plugin')
218
+ await framework.loadPlugin(new StoragePlugin())
219
+ console.log('[Bootstrap] Storage Plugin loaded')
220
+ } else {
221
+ console.log('[Bootstrap] Storage Plugin already loaded, skipping')
222
+ }
223
+
224
+ // 3. 内置工具插件
225
+ if (!framework.pluginManager.has('tools')) {
226
+ const { ToolsPlugin } = require('./tools-plugin')
227
+ await framework.loadPlugin(new ToolsPlugin())
228
+ console.log('[Bootstrap] Tools Plugin loaded')
229
+ } else {
230
+ console.log('[Bootstrap] Tools Plugin already loaded, skipping')
231
+ }
232
+
233
+ // 4. 工作流插件
234
+ if (!framework.pluginManager.has('workflow')) {
235
+ const { WorkflowPlugin } = require('../src/capabilities/workflow-engine')
236
+ await framework.loadPlugin(new WorkflowPlugin())
237
+ console.log('[Bootstrap] Workflow Plugin loaded')
238
+ } else {
239
+ console.log('[Bootstrap] Workflow Plugin already loaded, skipping')
240
+ }
241
+
242
+ // 5. Skill 管理器插件
243
+ if (!framework.pluginManager.has('skill-manager')) {
244
+ if (skillsDirs.length > 0) {
245
+ const { SkillManagerPlugin } = require('../src/capabilities/skill-manager')
246
+ // 传递所有 skills 目录
247
+ await framework.loadPlugin(new SkillManagerPlugin({ skillsDirs }))
248
+ console.log('[Bootstrap] Skill Manager loaded')
249
+ }
250
+ } else {
251
+ console.log('[Bootstrap] Skill Manager already loaded, skipping')
252
+ }
253
+
254
+ // 6. MCP 执行器插件(始终加载,确保 mcp_reload 工具可用)
255
+ if (!framework.pluginManager.has('mcp-executor')) {
256
+ const mcpServers = Object.entries(agentConfig.mcpServers || {})
257
+ const servers = mcpServers.map(([name, cfg]) => ({
258
+ ...cfg,
259
+ name,
260
+ command: cfg.command,
261
+ args: cfg.args || [],
262
+ env: cfg.env || {},
263
+ url: cfg.url,
264
+ headers: cfg.headers
265
+ }))
266
+ const { MCPExecutorPlugin } = require('../src/executors/mcp-executor')
267
+ await framework.loadPlugin(new MCPExecutorPlugin({ servers }))
268
+ console.log(`[Bootstrap] MCP Executor loaded${servers.length > 0 ? ` (${servers.length} servers)` : ' (no servers)'}`)
269
+ } else {
270
+ console.log('[Bootstrap] MCP Executor already loaded, skipping')
271
+ }
272
+
273
+ // 7. Shell 执行器插件
274
+ if (!framework.pluginManager.has('shell-executor')) {
275
+ const { ShellExecutorPlugin } = require('./shell-executor-plugin')
276
+ await framework.loadPlugin(new ShellExecutorPlugin())
277
+ console.log('[Bootstrap] Shell Executor loaded')
278
+ } else {
279
+ console.log('[Bootstrap] Shell Executor already loaded, skipping')
280
+ }
281
+
282
+ // 8. Python 执行器插件
283
+ if (!framework.pluginManager.has('python-executor')) {
284
+ const { PythonExecutorPlugin } = require('./python-executor-plugin')
285
+ await framework.loadPlugin(new PythonExecutorPlugin())
286
+ console.log('[Bootstrap] Python Executor loaded')
287
+ } else {
288
+ console.log('[Bootstrap] Python Executor already loaded, skipping')
289
+ }
290
+
291
+ // 9. Session 会话管理插件
292
+ if (!framework.pluginManager.has('session')) {
293
+ const { SessionPlugin } = require('./session-plugin')
294
+ await framework.loadPlugin(new SessionPlugin())
295
+ console.log('[Bootstrap] Session Plugin loaded')
296
+ } else {
297
+ console.log('[Bootstrap] Session Plugin already loaded, skipping')
298
+ }
299
+
300
+ // 10. Audit 审计日志插件
301
+ if (!framework.pluginManager.has('audit')) {
302
+ const { AuditPlugin } = require('./audit-plugin')
303
+ await framework.loadPlugin(new AuditPlugin())
304
+ console.log('[Bootstrap] Audit Plugin loaded')
305
+ } else {
306
+ console.log('[Bootstrap] Audit Plugin already loaded, skipping')
307
+ }
308
+
309
+ // 10. Rules 规则引擎插件
310
+ if (!framework.pluginManager.has('rules')) {
311
+ const { RulesPlugin } = require('./rules-plugin')
312
+ await framework.loadPlugin(new RulesPlugin())
313
+ console.log('[Bootstrap] Rules Plugin loaded')
314
+ } else {
315
+ console.log('[Bootstrap] Rules Plugin already loaded, skipping')
316
+ }
317
+
318
+ // 11. Scheduler 定时任务插件
319
+ if (!framework.pluginManager.has('scheduler')) {
320
+ const { SchedulerPlugin } = require('./scheduler-plugin')
321
+ await framework.loadPlugin(new SchedulerPlugin())
322
+ console.log('[Bootstrap] Scheduler Plugin loaded')
323
+ } else {
324
+ console.log('[Bootstrap] Scheduler Plugin already loaded, skipping')
325
+ }
326
+
327
+ // 11. FileSystem 文件系统插件
328
+ if (!framework.pluginManager.has('file-system')) {
329
+ const { FileSystemPlugin } = require('./file-system-plugin')
330
+ await framework.loadPlugin(new FileSystemPlugin())
331
+ console.log('[Bootstrap] FileSystem Plugin loaded')
332
+ } else {
333
+ console.log('[Bootstrap] FileSystem Plugin already loaded, skipping')
334
+ }
335
+
336
+ // 12. Think 主动思考插件
337
+ if (!framework.pluginManager.has('think')) {
338
+ const { ThinkPlugin } = require('./think-plugin')
339
+ await framework.loadPlugin(new ThinkPlugin())
340
+ console.log('[Bootstrap] Think Plugin loaded')
341
+ } else {
342
+ console.log('[Bootstrap] Think Plugin already loaded, skipping')
343
+ }
344
+
345
+ // 13. 加载自定义插件
346
+ await loadCustomPlugins(framework, agentConfig)
347
+
348
+ console.log('[Bootstrap] All plugins loaded')
349
+
350
+ // 统一启动所有插件(避免重复启动)
351
+ await framework.pluginManager.startAll()
352
+
353
+ // 清除 bootstrap 模式
354
+ framework.pluginManager.setBootstrapping(false)
355
+
356
+ console.log('[Bootstrap] Plugins:', framework.pluginManager.getAll().map(p => p.name))
357
+ console.log('[Bootstrap] Tools:', framework.getTools().map(t => t.name))
358
+ }
359
+
360
+ async function loadCustomPlugins(framework, agentConfig) {
361
+ // 加载 .agent/plugins 目录下的自定义插件
362
+ const pluginsDir = path.resolve(process.cwd(), '.agent', 'plugins')
363
+
364
+ if (!fs.existsSync(pluginsDir)) {
365
+ return
366
+ }
367
+
368
+ const files = fs.readdirSync(pluginsDir).filter(f => f.endsWith('.js'))
369
+
370
+ for (const file of files) {
371
+ try {
372
+ const pluginPath = path.join(pluginsDir, file)
373
+
374
+ // 清除缓存
375
+ delete require.cache[require.resolve(pluginPath)]
376
+ const pluginModule = require(pluginPath)
377
+
378
+ let plugin
379
+ if (typeof pluginModule === 'function') {
380
+ // 免引入写法:直接传递函数,让 loadPlugin 内部处理
381
+ plugin = pluginModule
382
+ } else if (pluginModule.default) {
383
+ plugin = pluginModule.default
384
+ } else {
385
+ plugin = pluginModule
386
+ }
387
+
388
+ // 获取插件名称
389
+ const tempPlugin = typeof plugin === 'function' && plugin.prototype?.name
390
+ ? new plugin() : plugin
391
+ const pluginName = tempPlugin.name || file.replace('.js', '')
392
+
393
+ // 如果插件已加载且已启动,跳过
394
+ if (framework.pluginManager.has(pluginName) &&
395
+ framework.pluginManager.get(pluginName)?._started) {
396
+ continue
397
+ }
398
+
399
+ console.log(`[Bootstrap] Loading custom plugin: ${file}`)
400
+ await framework.loadPlugin(plugin)
401
+ } catch (err) {
402
+ console.error(`[Bootstrap] Failed to load plugin ${file}:`, err.message)
403
+ }
404
+ }
405
+ }
406
+
407
+ module.exports = {
408
+ DefaultPlugins,
409
+ loadAgentConfig,
410
+ bootstrapDefaults,
411
+ loadCustomPlugins
412
+ }