foliko 1.1.92 → 2.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 (212) hide show
  1. package/.claude/settings.local.json +2 -1
  2. package/CLAUDE.md +56 -30
  3. package/REFACTORING_PLAN.md +645 -0
  4. package/docs/architecture.md +131 -0
  5. package/docs/migration.md +57 -0
  6. package/docs/public-api.md +138 -0
  7. package/docs/usage.md +385 -0
  8. package/examples/ambient-example.js +20 -137
  9. package/examples/basic.js +21 -48
  10. package/examples/bootstrap.js +16 -74
  11. package/examples/mcp-example.js +6 -29
  12. package/examples/skill-example.js +6 -19
  13. package/examples/workflow.js +8 -56
  14. package/package.json +8 -4
  15. package/plugins/README.md +49 -0
  16. package/plugins/{ambient-agent → ambient}/EventWatcher.js +1 -1
  17. package/plugins/{ambient-agent → ambient}/ExplorerLoop.js +3 -3
  18. package/plugins/{ambient-agent → ambient}/GoalManager.js +2 -2
  19. package/plugins/ambient/README.md +14 -0
  20. package/plugins/{ambient-agent → ambient}/Reflector.js +1 -1
  21. package/plugins/{ambient-agent → ambient}/StateStore.js +1 -1
  22. package/plugins/{ambient-agent → ambient}/index.js +2 -2
  23. package/plugins/{ai-plugin.js → core/ai/index.js} +14 -30
  24. package/plugins/{audit-plugin.js → core/audit/index.js} +3 -30
  25. package/plugins/{coordinator-plugin.js → core/coordinator/index.js} +3 -35
  26. package/plugins/core/default/bootstrap.js +202 -0
  27. package/plugins/core/default/config.js +220 -0
  28. package/plugins/core/default/index.js +58 -0
  29. package/plugins/core/mcp/index.js +1 -0
  30. package/plugins/{python-plugin-loader.js → core/python-loader/index.js} +7 -187
  31. package/plugins/{rules-plugin.js → core/rules/index.js} +121 -64
  32. package/plugins/{scheduler-plugin.js → core/scheduler/index.js} +12 -114
  33. package/plugins/{session-plugin.js → core/session/index.js} +9 -73
  34. package/{src/capabilities/skill-manager.js → plugins/core/skill-manager/index.js} +64 -18
  35. package/plugins/{storage-plugin.js → core/storage/index.js} +5 -29
  36. package/plugins/{subagent-plugin.js → core/sub-agent/index.js} +10 -171
  37. package/plugins/{think-plugin.js → core/think/index.js} +24 -91
  38. package/{src/capabilities/workflow-engine.js → plugins/core/workflow/index.js} +87 -85
  39. package/plugins/default-plugins.js +6 -720
  40. package/plugins/{data-splitter-plugin.js → executors/data-splitter/index.js} +9 -83
  41. package/plugins/{extension-executor-plugin.js → executors/extension/index.js} +13 -97
  42. package/plugins/{python-executor-plugin.js → executors/python/index.js} +6 -31
  43. package/plugins/{shell-executor-plugin.js → executors/shell/index.js} +2 -5
  44. package/plugins/install/README.md +9 -0
  45. package/plugins/{install-plugin.js → install/index.js} +3 -3
  46. package/plugins/{file-system-plugin.js → io/file-system/index.js} +34 -236
  47. package/plugins/{web-plugin.js → io/web/index.js} +11 -113
  48. package/plugins/memory/README.md +13 -0
  49. package/plugins/{memory-plugin.js → memory/index.js} +4 -18
  50. package/plugins/messaging/email/README.md +19 -0
  51. package/plugins/{email → messaging/email}/index.js +2 -2
  52. package/plugins/{feishu-plugin.js → messaging/feishu/index.js} +3 -3
  53. package/plugins/{qq-plugin.js → messaging/qq/index.js} +5 -16
  54. package/plugins/{telegram-plugin.js → messaging/telegram/index.js} +3 -3
  55. package/plugins/{weixin-plugin.js → messaging/weixin/index.js} +15 -15
  56. package/plugins/{plugin-manager-plugin.js → plugin-manager/index.js} +36 -180
  57. package/plugins/{tools-plugin.js → tools/index.js} +68 -116
  58. package/plugins/trading/README.md +15 -0
  59. package/plugins/{gate-trading.js → trading/index.js} +8 -8
  60. package/{examples → sandbox}/test-concurrent-chat.js +2 -2
  61. package/{examples → sandbox}/test-long-chat.js +2 -2
  62. package/{examples → sandbox}/test-session-chat.js +2 -2
  63. package/{examples → sandbox}/test-web-plugin.js +1 -1
  64. package/{examples → sandbox}/test-weixin-feishu.js +2 -2
  65. package/src/agent/base.js +56 -0
  66. package/src/{core/agent-chat.js → agent/chat.js} +11 -11
  67. package/src/{core/coordinator-manager.js → agent/coordinator.js} +3 -3
  68. package/src/agent/index.js +111 -0
  69. package/src/agent/main.js +337 -0
  70. package/src/agent/prompt.js +78 -0
  71. package/src/agent/sub.js +198 -0
  72. package/src/agent/worker.js +104 -0
  73. package/{cli/bin/foliko.js → src/cli/bin.js} +1 -1
  74. package/{cli/src → src/cli}/commands/chat.js +25 -21
  75. package/{cli/src → src/cli}/index.js +1 -0
  76. package/{cli/src → src/cli}/ui/chat-ui-old.js +40 -178
  77. package/{cli/src → src/cli}/ui/chat-ui.js +3 -3
  78. package/{cli/src → src/cli}/ui/components/footer-bar.js +1 -1
  79. package/src/{core → common}/constants.js +3 -0
  80. package/src/common/errors.js +402 -0
  81. package/src/{utils → common}/logger.js +33 -0
  82. package/src/{utils/chat-queue.js → common/queue.js} +2 -2
  83. package/src/config/plugin-config.js +50 -0
  84. package/src/context/agent.js +32 -0
  85. package/src/context/compaction-prompts.js +170 -0
  86. package/src/context/compaction-utils.js +191 -0
  87. package/src/context/compressor.js +413 -0
  88. package/src/context/index.js +9 -0
  89. package/src/{core/context-manager.js → context/manager.js} +1 -1
  90. package/src/context/request.js +50 -0
  91. package/src/context/session.js +33 -0
  92. package/src/context/storage.js +30 -0
  93. package/src/executors/mcp-client.js +153 -0
  94. package/src/executors/mcp-desc.js +236 -0
  95. package/src/executors/mcp-executor.js +91 -956
  96. package/src/{core → framework}/command-registry.js +1 -1
  97. package/src/framework/framework.js +300 -0
  98. package/src/framework/index.js +18 -0
  99. package/src/framework/lifecycle.js +203 -0
  100. package/src/framework/loader.js +78 -0
  101. package/src/framework/registry.js +86 -0
  102. package/src/{core/ui-extension-context.js → framework/ui-extension.js} +1 -1
  103. package/src/index.js +130 -15
  104. package/src/llm/index.js +26 -0
  105. package/src/llm/provider.js +212 -0
  106. package/src/llm/registry.js +11 -0
  107. package/src/{core/token-counter.js → llm/tokens.js} +4 -37
  108. package/src/{core/plugin-base.js → plugin/base.js} +10 -136
  109. package/src/plugin/index.js +14 -0
  110. package/src/plugin/loader.js +101 -0
  111. package/src/plugin/manager.js +261 -0
  112. package/src/{core → session}/branch-summary-auto.js +2 -2
  113. package/src/{core/chat-session.js → session/chat.js} +2 -2
  114. package/src/session/index.js +7 -0
  115. package/src/{core/session-manager.js → session/session.js} +2 -2
  116. package/src/session/ttl.js +92 -0
  117. package/src/{core/jsonl-storage.js → storage/jsonl.js} +1 -1
  118. package/src/tool/executor.js +85 -0
  119. package/src/tool/index.js +15 -0
  120. package/src/tool/registry.js +143 -0
  121. package/src/{core/tool-router.js → tool/router.js} +17 -124
  122. package/src/tool/schema.js +108 -0
  123. package/src/utils/data-splitter.js +1 -1
  124. package/src/utils/download.js +1 -1
  125. package/src/utils/index.js +6 -6
  126. package/src/utils/message-validator.js +1 -1
  127. package/tests/core/context-storage.test.js +46 -0
  128. package/tests/core/llm.test.js +54 -0
  129. package/tests/core/plugin.test.js +42 -0
  130. package/tests/core/tool.test.js +60 -0
  131. package/tests/setup.js +10 -0
  132. package/tests/smoke.test.js +58 -0
  133. package/vitest.config.js +9 -0
  134. package/cli/src/daemon.js +0 -149
  135. package/docs/CONTEXT_DESIGN.md +0 -1596
  136. package/docs/ai-sdk-optimization.md +0 -655
  137. package/docs/features.md +0 -120
  138. package/docs/qq-bot.md +0 -976
  139. package/docs/quick-reference.md +0 -160
  140. package/docs/user-manual.md +0 -1391
  141. package/images/geometric_shapes.jpg +0 -0
  142. package/images/sunset_mountain_lake.jpg +0 -0
  143. package/skills/poster-guide/SKILL.md +0 -792
  144. package/src/capabilities/index.js +0 -11
  145. package/src/core/agent.js +0 -808
  146. package/src/core/context-compressor.js +0 -959
  147. package/src/core/enhanced-context-compressor.js +0 -210
  148. package/src/core/framework.js +0 -1422
  149. package/src/core/index.js +0 -30
  150. package/src/core/plugin-manager.js +0 -961
  151. package/src/core/provider-registry.js +0 -159
  152. package/src/core/provider.js +0 -156
  153. package/src/core/request-context.js +0 -98
  154. package/src/core/subagent.js +0 -442
  155. package/src/core/system-prompt-builder.js +0 -120
  156. package/src/core/tool-executor.js +0 -202
  157. package/src/core/tool-registry.js +0 -517
  158. package/src/core/worker-agent.js +0 -192
  159. package/src/executors/executor-base.js +0 -58
  160. package/src/utils/error-boundary.js +0 -363
  161. package/src/utils/error.js +0 -374
  162. package/system.md +0 -1645
  163. package/website_v2/README.md +0 -57
  164. package/website_v2/SPEC.md +0 -1
  165. package/website_v2/docs/api.html +0 -128
  166. package/website_v2/docs/configuration.html +0 -147
  167. package/website_v2/docs/plugin-development.html +0 -129
  168. package/website_v2/docs/project-structure.html +0 -89
  169. package/website_v2/docs/skill-development.html +0 -85
  170. package/website_v2/index.html +0 -489
  171. package/website_v2/scripts/main.js +0 -93
  172. package/website_v2/styles/animations.css +0 -8
  173. package/website_v2/styles/docs.css +0 -83
  174. package/website_v2/styles/main.css +0 -417
  175. package/xhs_auth.json +0 -268
  176. package//346/265/267/346/212/245/346/217/222/344/273/266.md +0 -621
  177. /package/plugins/{ambient-agent → ambient}/constants.js +0 -0
  178. /package/plugins/{email → messaging/email}/constants.js +0 -0
  179. /package/plugins/{email → messaging/email}/handlers.js +0 -0
  180. /package/plugins/{email → messaging/email}/monitor.js +0 -0
  181. /package/plugins/{email → messaging/email}/parser.js +0 -0
  182. /package/plugins/{email → messaging/email}/reply.js +0 -0
  183. /package/plugins/{email → messaging/email}/utils.js +0 -0
  184. /package/{examples → sandbox}/test-chat.js +0 -0
  185. /package/{examples → sandbox}/test-mcp.js +0 -0
  186. /package/{examples → sandbox}/test-reload.js +0 -0
  187. /package/{examples → sandbox}/test-telegram.js +0 -0
  188. /package/{examples → sandbox}/test-tg-bot.js +0 -0
  189. /package/{examples → sandbox}/test-tg-simple.js +0 -0
  190. /package/{examples → sandbox}/test-tg.js +0 -0
  191. /package/{examples → sandbox}/test-think.js +0 -0
  192. /package/src/{core/sub-agent-config.js → agent/sub-config.js} +0 -0
  193. /package/{cli/src → src/cli}/commands/daemon.js +0 -0
  194. /package/{cli/src → src/cli}/commands/list.js +0 -0
  195. /package/{cli/src → src/cli}/commands/plugin.js +0 -0
  196. /package/{cli/src → src/cli}/ui/components/agent-mention-provider.js +0 -0
  197. /package/{cli/src → src/cli}/ui/components/chained-autocomplete-provider.js +0 -0
  198. /package/{cli/src → src/cli}/ui/components/message-bubble.js +0 -0
  199. /package/{cli/src → src/cli}/ui/components/status-bar.js +0 -0
  200. /package/{cli/src → src/cli}/utils/ansi.js +0 -0
  201. /package/{cli/src → src/cli}/utils/config.js +0 -0
  202. /package/{cli/src → src/cli}/utils/markdown.js +0 -0
  203. /package/{cli/src → src/cli}/utils/plugin-config.js +0 -0
  204. /package/{cli/src → src/cli}/utils/render-diff.js +0 -0
  205. /package/src/{utils/circuit-breaker.js → common/circuit.js} +0 -0
  206. /package/src/{utils/edit-diff.js → common/diff.js} +0 -0
  207. /package/src/{utils/event-emitter.js → common/events.js} +0 -0
  208. /package/src/{utils → common}/id.js +0 -0
  209. /package/src/{utils → common}/retry.js +0 -0
  210. /package/src/{core/notification-manager.js → notification/manager.js} +0 -0
  211. /package/src/{core/session-entry.js → session/entry.js} +0 -0
  212. /package/src/{core/storage-manager.js → storage/manager.js} +0 -0
@@ -1,724 +1,10 @@
1
- /**
2
- * 默认插件配置加载器
3
- * 检测 .foliko/ 目录下的配置,提供给 bootstrap() 使用
4
- */
5
-
6
- const fs = require('fs')
7
- const path = require('path')
8
- const { Plugin } = require('../src/core/plugin-base')
9
- const { logger } = require('../src/utils/logger')
10
- const log = logger.child('AgentConfig')
11
- const bootstrapLog = logger.child('Bootstrap')
12
-
13
- /**
14
- * 加载 .foliko 目录下的配置
15
- * 返回配置对象,可用于手动加载插件
16
- */
17
- function loadAgentConfig(framework, agentDir = '.foliko') {
18
- // 向后兼容:旧调用 loadAgentConfig('.foliko') 时把 framework 当成 agentDir
19
- if (typeof framework === 'string') {
20
- agentDir = framework;
21
- framework = null;
22
- }
23
-
24
- const config = {
25
- agentDir,
26
- ai: {},
27
- mcpServers: {},
28
- plugins: [],
29
- skillsDirs: [],
30
- agentsDir: null, // 子Agent配置目录
31
- pluginLinks: {} // 外部插件链接 { "插件名": "外部路径" }
32
- }
33
- const cwd = framework?.getCwd?.() ?? process.cwd()
34
- const cmdDir = path.resolve(cwd)
35
- const resolvedDir = path.resolve(cwd, agentDir)
36
-
37
- //log.info(` Loading config from: ${resolvedDir}`)
38
-
39
- // 添加 agents 目录(如果存在)
40
- const agentsDir = path.join(resolvedDir, 'agents')
41
- if (fs.existsSync(agentsDir)) {
42
- config.agentsDir = agentsDir
43
- //log.info(` Found agents directory: ${agentsDir}`)
44
- }
45
-
46
- // 加载 config 文件(key=value 格式)
47
- const configFile = path.join(resolvedDir, 'config')
48
- if (fs.existsSync(configFile)) {
49
- try {
50
- const lines = fs.readFileSync(configFile, 'utf-8').split('\n')
51
- for (const line of lines) {
52
- const trimmed = line.trim()
53
- if (!trimmed || trimmed.startsWith('#')) continue
54
-
55
- const colonIndex = trimmed.indexOf(':')
56
- if (colonIndex === -1) continue
57
-
58
- const key = trimmed.substring(0, colonIndex).trim()
59
- const value = trimmed.substring(colonIndex + 1).trim()
60
-
61
- if (key === 'ai_key' || key === 'api_key') {
62
- config.ai.apiKey = value
63
- } else if (key === 'ai_model' || key === 'model') {
64
- config.ai.model = value
65
- } else if (key === 'ai_provider' || key === 'provider') {
66
- config.ai.provider = value
67
- } else if (key === 'ai_base_url' || key === 'baseURL') {
68
- config.ai.baseURL = value
69
- }
70
- }
71
- } catch (err) {
72
- log.error(` Failed to load config:`, err.message)
73
- }
74
- }
75
-
76
- // 加载 ai.json
77
- const aiFile = path.join(resolvedDir, 'ai.json')
78
- if (fs.existsSync(aiFile)) {
79
- try {
80
- const content = fs.readFileSync(aiFile, 'utf-8')
81
- const aiConfig = JSON.parse(content)
82
- config.ai = { ...config.ai, ...aiConfig }
83
- } catch (err) {
84
- log.error(` Failed to load ai.json:`, err.message)
85
- }
86
- }
87
-
88
- // 加载 plugins.json (支持 telegram, weixin, email 等插件配置)
89
- const pluginsFile = path.join(resolvedDir, 'plugins.json')
90
- if (fs.existsSync(pluginsFile)) {
91
- try {
92
- const content = fs.readFileSync(pluginsFile, 'utf-8')
93
- const pluginsConfig = JSON.parse(content)
94
- // telegram 配置
95
- if (pluginsConfig.telegram) {
96
- config.telegram = pluginsConfig.telegram
97
- }
98
- // weixin 配置
99
- if (pluginsConfig.weixin) {
100
- config.weixin = pluginsConfig.weixin
101
- }
102
- // email 配置
103
- if (pluginsConfig.email) {
104
- config.email = pluginsConfig.email
105
- }
106
- // 外部插件链接配置 (pluginLinks: { "poster": "/path/to/poster-plugin" })
107
- if (pluginsConfig.pluginLinks) {
108
- config.pluginLinks = pluginsConfig.pluginLinks
109
- //log.info(` Found plugin link: ${name} -> ${targetPath}`)
110
- }
111
- } catch (err) {
112
- log.error(` Failed to load plugins.json:`, err.message)
113
- }
114
- }
115
-
116
- // 加载 weixin.json (如果存在)
117
- const weixinFile = path.join(resolvedDir, 'weixin.json')
118
- if (fs.existsSync(weixinFile)) {
119
- try {
120
- const content = fs.readFileSync(weixinFile, 'utf-8')
121
- config.weixin = { ...config.weixin, ...JSON.parse(content) }
122
- } catch (err) {
123
- log.error(` Failed to load weixin.json:`, err.message)
124
- }
125
- }
126
-
127
- // 加载 mcp_config.json
128
- const mcpFile = path.join(resolvedDir, 'mcp_config.json')
129
- if (fs.existsSync(mcpFile)) {
130
- try {
131
- const content = fs.readFileSync(mcpFile, 'utf-8')
132
- const mcpConfig = JSON.parse(content)
133
- config.mcpServers = mcpConfig.mcpServers || {}
134
- } catch (err) {
135
- log.error(` Failed to load mcp_config.json:`, err.message)
136
- }
137
- }
138
-
139
- // 添加 .foliko/skills 目录(不存在则创建)
140
- // 使用相对路径存储,这样 setCwd 时能自动随新 cwd 重新解析
141
- const projectSkillsRel = path.join(agentDir, 'skills')
142
- const projectSkillsAbs = path.resolve(cwd, projectSkillsRel)
143
- if (fs.existsSync(resolvedDir) && !fs.existsSync(projectSkillsAbs)) {
144
- fs.mkdirSync(projectSkillsAbs, { recursive: true })
145
- //log.info(` Created skills directory: ${projectSkillsAbs}`)
146
- }
147
- if (fs.existsSync(projectSkillsAbs)) {
148
- config.skillsDirs.push(projectSkillsRel)
149
- }
150
-
151
- // 添加 cwd 下的 skills/ 目录(相对路径,与 setCwd 联动)
152
- const cmdskillsRel = 'skills'
153
- const cmdskillsAbs = path.resolve(cwd, cmdskillsRel)
154
- if (fs.existsSync(cmdskillsAbs)) {
155
- config.skillsDirs.push(cmdskillsRel)
156
- }
157
-
158
- // 添加框架根目录的 skills/ 目录(绝对路径,与 cwd 无关)
159
- const parentDir = path.dirname(__dirname)
160
- const rootSkillsDir = path.join(parentDir, 'skills')
161
- if (fs.existsSync(rootSkillsDir)) {
162
- //log.info(` Found root skills directory: ${rootSkillsDir}`)
163
- config.skillsDirs.push(rootSkillsDir)
164
- }
165
-
166
- // const defaultSkillsDir = path.join(process.cwd(), 'defaultSkills')
167
- // if (fs.existsSync(defaultSkillsDir)) {
168
- // config.skillsDirs.push(defaultSkillsDir)
169
- // }
170
-
171
- // ============ HOME DIR (~/.foliko/) SCAN ============
172
- // Home 是独立配置源:
173
- // - 单文件配置:项目不存在时 home 作为回退
174
- // - 多文件资源(skills/agents/plugins):两个目录都扫描
175
- const homeDir = framework?.getHomeAgentDir?.()
176
- const isHomeSameAsProject = homeDir && path.resolve(homeDir) === resolvedDir
177
- if (homeDir && fs.existsSync(homeDir) && !isHomeSameAsProject) {
178
- // 单文件配置回退
179
- const homeConfig = path.join(homeDir, 'config')
180
- if (!fs.existsSync(path.join(resolvedDir, 'config')) && fs.existsSync(homeConfig)) {
181
- try {
182
- const lines = fs.readFileSync(homeConfig, 'utf-8').split('\n')
183
- for (const line of lines) {
184
- const trimmed = line.trim()
185
- if (!trimmed || trimmed.startsWith('#')) continue
186
- const colonIndex = trimmed.indexOf(':')
187
- if (colonIndex === -1) continue
188
- const key = trimmed.substring(0, colonIndex).trim()
189
- const value = trimmed.substring(colonIndex + 1).trim()
190
- if (key === 'ai_key' || key === 'api_key') {
191
- if (!config.ai.apiKey) config.ai.apiKey = value
192
- } else if (key === 'ai_model' || key === 'model') {
193
- if (!config.ai.model) config.ai.model = value
194
- } else if (key === 'ai_provider' || key === 'provider') {
195
- if (!config.ai.provider) config.ai.provider = value
196
- } else if (key === 'ai_base_url' || key === 'baseURL') {
197
- if (!config.ai.baseURL) config.ai.baseURL = value
198
- }
199
- }
200
- } catch (err) {
201
- log.error(' Failed to load home config:', err.message)
202
- }
203
- }
204
-
205
- const homeAi = path.join(homeDir, 'ai.json')
206
- if (!fs.existsSync(path.join(resolvedDir, 'ai.json')) && fs.existsSync(homeAi)) {
207
- try {
208
- const homeObj = JSON.parse(fs.readFileSync(homeAi, 'utf-8'))
209
- config.ai = { ...homeObj, ...config.ai }
210
- } catch (err) {
211
- log.error(' Failed to load home ai.json:', err.message)
212
- }
213
- }
214
-
215
- const homePluginsFile = path.join(homeDir, 'plugins.json')
216
- if (!fs.existsSync(path.join(resolvedDir, 'plugins.json')) && fs.existsSync(homePluginsFile)) {
217
- try {
218
- const pc = JSON.parse(fs.readFileSync(homePluginsFile, 'utf-8'))
219
- if (pc.telegram && !config.telegram) config.telegram = pc.telegram
220
- if (pc.weixin && !config.weixin) config.weixin = pc.weixin
221
- if (pc.email && !config.email) config.email = pc.email
222
- config.pluginLinks = { ...(pc.pluginLinks || {}), ...config.pluginLinks }
223
- } catch (err) {
224
- log.error(' Failed to load home plugins.json:', err.message)
225
- }
226
- }
227
-
228
- const homeWeixin = path.join(homeDir, 'weixin.json')
229
- if (!fs.existsSync(path.join(resolvedDir, 'weixin.json')) && fs.existsSync(homeWeixin)) {
230
- try {
231
- const w = JSON.parse(fs.readFileSync(homeWeixin, 'utf-8'))
232
- config.weixin = { ...w, ...config.weixin }
233
- } catch (err) {
234
- log.error(' Failed to load home weixin.json:', err.message)
235
- }
236
- }
237
-
238
- const homeMcp = path.join(homeDir, 'mcp_config.json')
239
- if (!fs.existsSync(path.join(resolvedDir, 'mcp_config.json')) && fs.existsSync(homeMcp)) {
240
- try {
241
- const m = JSON.parse(fs.readFileSync(homeMcp, 'utf-8'))
242
- config.mcpServers = { ...(m.mcpServers || {}), ...config.mcpServers }
243
- } catch (err) {
244
- log.error(' Failed to load home mcp_config.json:', err.message)
245
- }
246
- }
247
-
248
- // agents 目录:home 仅在项目无 agentsDir 时生效
249
- const homeAgentsDir = path.join(homeDir, 'agents')
250
- if (!config.agentsDir && fs.existsSync(homeAgentsDir)) {
251
- config.agentsDir = homeAgentsDir
252
- }
253
-
254
- // skills 目录:项目 .foliko/skills 之后插入 home
255
- const homeSkillsDir = path.join(homeDir, 'skills')
256
- if (!fs.existsSync(homeSkillsDir)) {
257
- try { fs.mkdirSync(homeSkillsDir, { recursive: true }) } catch (err) { /* ignore */ }
258
- }
259
- if (fs.existsSync(homeSkillsDir)) {
260
- const projectFolikoSkillsIdx = config.skillsDirs.findIndex(
261
- d => d === path.join(resolvedDir, 'skills')
262
- )
263
- const insertAt = projectFolikoSkillsIdx >= 0
264
- ? projectFolikoSkillsIdx + 1
265
- : config.skillsDirs.length
266
- config.skillsDirs.splice(insertAt, 0, homeSkillsDir)
267
- }
268
-
269
- // home plugins:扫描并加入 pluginLinks
270
- const homePluginsDir = path.join(homeDir, 'plugins')
271
- if (fs.existsSync(homePluginsDir)) {
272
- for (const name of scanPluginNames(homePluginsDir)) {
273
- const resolvedPlugin = resolvePluginPath(homePluginsDir, name)
274
- if (resolvedPlugin) config.pluginLinks[name] = resolvedPlugin.path
275
- }
276
- }
277
- }
278
-
279
- return config
280
- }
281
-
282
- /**
283
- * DefaultPlugins - 默认插件配置加载器
284
- */
285
- class DefaultPlugins extends Plugin {
286
- constructor(config = {}) {
287
- super()
288
- this.name = 'defaults'
289
- this.version = '1.0.0'
290
- this.description = '默认插件配置加载器'
291
- this.priority = 0 // 最先加载
292
- this.system = true
293
-
294
- this._framework = null
295
- this._agentDir = config.agentDir || '.foliko'
296
- this._config = null
297
- }
298
-
299
- install(framework) {
300
- this._framework = framework
301
- return this
302
- }
303
-
304
- start(framework) {
305
- // 加载配置
306
- this._config = loadAgentConfig(this._framework, this._agentDir)
307
- return this
308
- }
309
-
310
- /**
311
- * 获取加载的配置
312
- */
313
- getConfig() {
314
- return this._config || loadAgentConfig(this._framework, this._agentDir)
315
- }
316
-
317
- reload(framework) {
318
- this._framework = framework
319
- this._config = loadAgentConfig(this._framework, this._agentDir)
320
- }
321
-
322
- uninstall(framework) {
323
- this._framework = null
324
- this._config = null
325
- }
326
- }
327
-
328
- /**
329
- * Bootstrap 辅助函数
330
- * 根据配置加载所有默认插件
331
- */
332
- async function bootstrapDefaults(framework, config = {}) {
333
- // 如果已经有配置,使用现有配置;否则加载
334
- const agentConfig = config._config
335
- if (!agentConfig) {
336
- bootstrapLog.error(' No config provided, skipping plugin loading')
337
- return
338
- }
339
- // 设置 bootstrap 模式,避免重复启动
340
- framework.pluginManager.setBootstrapping(true)
341
-
342
- // 合并 AI 配置
343
- const aiConfig = { ...agentConfig.ai, ...config.aiConfig }
344
-
345
- // 核心插件列表(不能禁用,必须加载)
346
- const CORE_PLUGINS = new Set([
347
- 'install', 'ai', 'storage', 'tools', 'workflow', 'skill-manager',
348
- 'mcp', 'extension-executor', 'shell-executor', 'python-executor', 'session', 'web',
349
- 'audit', 'rules', 'scheduler', 'file-system', 'think', 'ambient',
350
- 'python-plugin-loader', 'telegram', 'weixin', 'subagent-manager',
351
- 'memory', 'coordinator'
352
- ])
353
-
354
- // 辅助函数:检查插件是否应该加载
355
- // 传入插件名称(字符串)、已创建的实例,或插件类
356
- const shouldLoad = (plugin) => {
357
- const name = typeof plugin === 'string' ? plugin : (plugin.name || plugin.prototype?.name)
358
- if (framework.pluginManager.has(name)) {
359
- framework._debug&&bootstrapLog.debug(` ${name} Plugin already loaded, skipping`)
360
- return false
361
- }
362
- // 系统插件(system: true)不能禁用
363
- let isSystem = false
364
- if (typeof plugin === 'function') {
365
- isSystem = plugin.prototype?.system === true
366
- } else if (typeof plugin === 'object') {
367
- isSystem = plugin.system === true
368
- }
369
- if (isSystem && framework.pluginManager.isEnabled(name) === false) {
370
- framework._debug&&bootstrapLog.debug(` ${name} is a system plugin, cannot be disabled`)
371
- }
372
- return true
373
- }
374
-
375
- // 0. Install 工具插件(最先加载,让其他插件能用到它的 node_modules)
376
- if (shouldLoad('install')) {
377
- const InstallPlugin = require('./install-plugin')
378
- await framework.loadPlugin(new InstallPlugin({ agentDir: agentConfig.agentDir }))
379
- framework._debug&&bootstrapLog.debug(' Install Plugin loaded')
380
- }
381
-
382
- // 合并 skillsDirs 配置(bootstrap 传入的优先)
383
- const skillsDirs = [
384
- ...(config.skillsDirs || []),
385
- ...(agentConfig.skillsDirs || [])
386
- ]
387
-
388
- framework._debug&&bootstrapLog.debug(' Loading default plugins...')
389
- // AI 插件(如果已禁用则跳过)
390
- // 根据 provider 获取对应的 API key
391
- const upperProvider = (aiConfig.provider || 'deepseek').toUpperCase().replace(/-/g, '_')
392
- const envApiKey = process.env[`${upperProvider}_API_KEY`] || process.env.FOLIKO_API_KEY
393
- if (!shouldLoad('ai') || !(aiConfig.provider || aiConfig.model || aiConfig.apiKey || envApiKey)) {
394
- // 跳过或已禁用
395
- } else {
396
- const AIPlugin = require('./ai-plugin')
397
- const aiPlugin = new AIPlugin({
398
- provider: aiConfig.provider || 'deepseek',
399
- model: aiConfig.model || 'deepseek-chat',
400
- apiKey: aiConfig.apiKey || envApiKey,
401
- baseURL: aiConfig.baseURL
402
- })
403
- await framework.loadPlugin(aiPlugin)
404
- framework._debug&&bootstrapLog.debug(' AI Plugin loaded')
405
- }
406
-
407
- // 1.5 创建主 Agent(供 Telegram 等需要绑定 Agent 的插件使用)
408
- // 注意:即使没有 AI 配置也创建主 agent,让 memory-plugin 等能注册 prompt parts
409
- if (!framework._mainAgent) {
410
- const { Agent } = require('../src/core/agent')
411
- const aiPlugin = framework.pluginManager.get('ai')
412
- const aiClient = aiPlugin ? aiPlugin.getAIClient() : null
413
- framework._debug&&bootstrapLog.debug(' Creating Main Agent - aiClient:', !!aiClient)
414
-
415
- framework._mainAgent = framework.createAgent({
416
- name: 'MainAgent',
417
- systemPrompt: '你是一个智能助手。当用户提出问题或任务时,你会主动分析需求,选择合适的工具来获取信息或执行操作。你善于将复杂任务拆解为多个步骤,通过工具协作完成。',
418
- model: aiConfig.model || 'deepseek-chat',
419
- provider: aiConfig.provider || 'deepseek',
420
- apiKey: aiPlugin ? aiPlugin.config.apiKey : (aiConfig.apiKey || envApiKey),
421
- baseURL: aiConfig.baseURL
422
- })
423
- framework._agents.push(framework._mainAgent)
424
- framework._debug&&bootstrapLog.debug(' Main Agent created, has _chatHandler:', !!framework._mainAgent._chatHandler)
425
- }
426
-
427
- // 2. Skill 管理器插件(需要 skillsDirs 配置)
428
- if (shouldLoad('skill-manager') && skillsDirs.length > 0) {
429
- const { SkillManagerPlugin } = require('../src/capabilities/skill-manager')
430
- await framework.loadPlugin(new SkillManagerPlugin({ skillsDirs }))
431
- framework._debug&&bootstrapLog.debug(' Skill Manager loaded')
432
- }
433
-
434
- // 3. MCP 执行器插件(需要转换 mcpServers 格式)
435
- if (shouldLoad('mcp')) {
436
- const mcpServers = Object.entries(agentConfig.mcpServers || {})
437
- const servers = mcpServers.map(([name, cfg]) => ({
438
- ...cfg,
439
- name,
440
- command: cfg.command,
441
- args: cfg.args || [],
442
- env: cfg.env || {},
443
- url: cfg.url,
444
- headers: cfg.headers,
445
- enabled: cfg.enabled !== false // 默认为 true
446
- }))
447
- const { MCPExecutorPlugin } = require('../src/executors/mcp-executor')
448
- await framework.loadPlugin(new MCPExecutorPlugin({ servers }))
449
- framework._debug&&bootstrapLog.debug(` MCP Executor loaded${servers.length > 0 ? ` (${servers.length} servers)` : ' (no servers)'}`)
450
- }
451
-
452
- // 4. Python 插件加载器(需要 agentDir)
453
- if (shouldLoad('python-plugin-loader')) {
454
- const PythonPluginLoader = require('./python-plugin-loader')
455
- await framework.loadPlugin(new PythonPluginLoader({ agentDir: agentConfig.agentDir }))
456
- framework._debug&&bootstrapLog.debug(' Python Plugin Loader loaded')
457
- }
458
-
459
- // 5. SubAgent 管理器(需要特殊初始化 SubAgentConfigManager)
460
- if (shouldLoad('subagent-manager')) {
461
- try {
462
- const { SubAgentConfigManager } = require('../src/core/sub-agent-config')
463
- const subAgentConfigManager = new SubAgentConfigManager(agentConfig.agentsDir)
464
- subAgentConfigManager.loadAll()
465
- framework._subAgentConfigManager = subAgentConfigManager
466
-
467
- const { SubAgentManagerPlugin } = require('./subagent-plugin')
468
- await framework.loadPlugin(new SubAgentManagerPlugin({ agentsDir: agentConfig.agentsDir }))
469
- framework._debug&&bootstrapLog.debug(' SubAgent Manager loaded')
470
- } catch (err) {
471
- bootstrapLog.warn(' SubAgent Manager failed:', err.message)
472
- }
473
- }
474
-
475
- // 6. 大数据分拆插件
476
- if (shouldLoad('data-splitter')) {
477
- const { DataSplitterPlugin } = require('./data-splitter-plugin')
478
- await framework.loadPlugin(new DataSplitterPlugin({
479
- autoSplitThreshold: agentConfig.dataSplitter?.autoSplitThreshold || 50000,
480
- chunkSize: agentConfig.dataSplitter?.chunkSize || 60000,
481
- maxConcurrent: agentConfig.dataSplitter?.maxConcurrent || 3,
482
- }))
483
- framework._debug&&bootstrapLog.debug(' DataSplitter Plugin loaded')
484
- }
485
-
486
- // 7. 自动加载 plugins/ 目录下的所有插件
487
- await loadCustomPlugins(framework, agentConfig)
488
-
489
- framework._debug&&bootstrapLog.debug(' All plugins loaded')
490
-
491
- // 统一启动所有插件(避免重复启动)
492
- await framework.pluginManager.startAll()
493
-
494
- // 清除 bootstrap 模式
495
- framework.pluginManager.setBootstrapping(false)
496
-
497
- framework._debug&&bootstrapLog.debug(' Loaded plugins ', framework.pluginManager.getAll().length)
498
- framework._debug&&bootstrapLog.debug(' Loaded tools ', framework.getTools().length)
499
- }
1
+ 'use strict';
500
2
 
501
3
  /**
502
- * 解析插件路径
503
- * 支持两种结构:
504
- * 1. 文件夹结构: .foliko/plugins/my-plugin/index.js
505
- * 2. 单文件结构: .foliko/plugins/my-plugin.js
506
- * 支持符号链接
507
- * @param {string} pluginsDir - 插件目录
508
- * @param {string} name - 插件名称
509
- * @returns {{path: string, type: 'folder'|'file'}|null} 插件路径和类型
4
+ * default-plugins.js — 向后兼容转发
5
+ * 实际实现在 plugins/core/default/
6
+ * @deprecated 使用 plugins/core/default
510
7
  */
511
- function resolvePluginPath(pluginsDir, name) {
512
- const folderPath = path.join(pluginsDir, name)
513
- const filePath = path.join(pluginsDir, `${name}.js`)
514
-
515
- // 检查文件夹是否存在
516
- if (!fs.existsSync(folderPath)) {
517
- // 单文件回退
518
- if (fs.existsSync(filePath)) {
519
- return { path: filePath, type: 'file' }
520
- }
521
- return null
522
- }
523
-
524
- // 检查是否为目录(支持符号链接)
525
- let isDir = false
526
- try {
527
- isDir = fs.statSync(folderPath).isDirectory()
528
- } catch (err) {
529
- // 无法访问,跳过
530
- if (fs.existsSync(filePath)) {
531
- return { path: filePath, type: 'file' }
532
- }
533
- return null
534
- }
535
-
536
- if (isDir) {
537
- const pkgPath = path.join(folderPath, 'package.json')
538
- if (fs.existsSync(pkgPath)) {
539
- try {
540
- const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'))
541
- const main = pkg.main || 'index.js'
542
- const mainPath = path.join(folderPath, main)
543
- if (fs.existsSync(mainPath)) {
544
- return { path: mainPath, type: 'folder' }
545
- }
546
- } catch (err) {
547
- log.warn(` Failed to parse package.json for ${name}:`, err.message)
548
- }
549
- }
550
- // 默认加载 index.js
551
- const indexPath = path.join(folderPath, 'index.js')
552
- if (fs.existsSync(indexPath)) {
553
- return { path: indexPath, type: 'folder' }
554
- }
555
- return null
556
- }
557
-
558
- // 单文件回退
559
- if (fs.existsSync(filePath)) {
560
- return { path: filePath, type: 'file' }
561
- }
562
-
563
- return null
564
- }
565
-
566
- /**
567
- * 扫描插件目录,返回所有插件名称
568
- * 支持符号链接
569
- * @param {string} pluginsDir - 插件目录
570
- * @returns {string[]} 插件名称列表
571
- */
572
- function scanPluginNames(pluginsDir) {
573
- if (!fs.existsSync(pluginsDir)) {
574
- return []
575
- }
576
-
577
- const names = new Set()
578
- const entries = fs.readdirSync(pluginsDir, { withFileTypes: true })
579
-
580
- for (const entry of entries) {
581
- const fullPath = path.join(pluginsDir, entry.name)
582
-
583
- // 检查是否为符号链接
584
- let isSymlink = false
585
- try {
586
- const lstat = fs.lstatSync(fullPath)
587
- isSymlink = lstat.isSymbolicLink()
588
- } catch (err) {
589
- // 无法获取信息,跳过
590
- continue
591
- }
592
-
593
- // 获取实际类型(跟随符号链接)
594
- let isDir = entry.isDirectory()
595
- if (isSymlink) {
596
- try {
597
- isDir = fs.statSync(fullPath).isDirectory()
598
- } catch (err) {
599
- // 符号链接目标不存在,跳过
600
- continue
601
- }
602
- }
603
-
604
- if (isDir) {
605
- // 文件夹插件(包括符号链接指向的目录)
606
- names.add(entry.name)
607
- } else if (entry.isFile() && entry.name.endsWith('.js')) {
608
- // 单文件插件(排除与文件夹同名的)
609
- const baseName = entry.name.replace(/\.js$/, '')
610
- if (!names.has(baseName)) {
611
- names.add(baseName)
612
- }
613
- }
614
- }
615
-
616
- return Array.from(names)
617
- }
618
-
619
- async function loadCustomPlugins(framework, agentConfig) {
620
- const { Plugin } = require('../src/core/plugin-base')
621
-
622
- const cwd = framework?.getCwd?.() ?? process.cwd()
623
-
624
- // 加载两个目录下的自定义插件:
625
- // 1. .foliko/plugins/ - 用户自定义插件(强制启用)
626
- // 2. plugins/ - 项目内置插件(自动加载)
627
- const dirs = [
628
- { dir: path.resolve(cwd, '.foliko', 'plugins'), forceEnabled: true },
629
- { dir: path.resolve(cwd, 'plugins'), forceEnabled: false },
630
- { dir: path.resolve(__dirname, '..', 'plugins'), forceEnabled: false }
631
- // 项目下的 plugins 目录(兼容旧版本)
632
-
633
- ]
634
-
635
- for (const { dir, forceEnabled } of dirs) {
636
- if (!fs.existsSync(dir)) {
637
- continue
638
- }
639
-
640
- // 扫描所有插件名称(支持文件夹和单文件)
641
- const pluginNames = scanPluginNames(dir)
642
-
643
- for (const pluginName of pluginNames) {
644
- try {
645
- const resolved = resolvePluginPath(dir, pluginName)
646
- if (!resolved) {
647
- continue
648
- }
649
-
650
- const { path: pluginPath, type } = resolved
651
-
652
- // 清除缓存
653
- delete require.cache[require.resolve(pluginPath)]
654
- const pluginModule = require(pluginPath)
655
-
656
- let plugin
657
- if (typeof pluginModule === 'function') {
658
- plugin = pluginModule
659
- } else if (pluginModule.default) {
660
- plugin = pluginModule.default
661
- } else {
662
- // 支持具名导出 { PluginClass } 或 { PluginClass, Other }
663
- // 找到第一个是 Plugin 子类的导出
664
- const keys = Object.keys(pluginModule)
665
- let foundPlugin = null
666
- for (const key of keys) {
667
- const exp = pluginModule[key]
668
- if (typeof exp === 'function' && exp.prototype instanceof Plugin) {
669
- foundPlugin = exp
670
- break
671
- }
672
- }
673
- plugin = foundPlugin || pluginModule
674
- }
675
-
676
- // 获取插件名称(参考 plugin-manager 的逻辑)
677
- let resolvedPluginName = pluginName
678
- if (typeof plugin === 'function' && plugin.prototype instanceof Plugin) {
679
- // Plugin 子类:实例化获取名称
680
- const instance = new plugin()
681
- resolvedPluginName = instance.name || pluginName
682
- } else if (typeof plugin === 'function') {
683
- // 工厂函数
684
- const result = plugin(Plugin)
685
- resolvedPluginName = result?.name || pluginName
686
- } else {
687
- resolvedPluginName = plugin?.name || pluginName
688
- }
689
-
690
- // 如果插件已加载且已启动,跳过
691
- if (framework.pluginManager.has(resolvedPluginName) &&
692
- framework.pluginManager.get(resolvedPluginName)?._started) {
693
- continue
694
- }
695
-
696
- bootstrapLog.debug(` Loading plugin: ${resolvedPluginName} (${type})`)
697
-
698
- // 从 agentConfig 获取插件配置
699
- const pluginConfig = agentConfig[resolvedPluginName] || {}
700
-
701
- // 实例化插件并传入配置
702
- let pluginInstance
703
- if (typeof plugin === 'function' && plugin.prototype instanceof Plugin) {
704
- pluginInstance = new plugin(pluginConfig)
705
- } else {
706
- pluginInstance = plugin
707
- }
708
-
709
- await framework.pluginManager.load(pluginInstance, { forceEnabled })
710
- } catch (err) {
711
- log.error(` Failed to load plugin ${pluginName}:`, err.message)
712
- }
713
- }
714
- }
715
- }
716
8
 
717
- module.exports = {
718
- DefaultPlugins,
719
- loadAgentConfig,
720
- bootstrapDefaults,
721
- loadCustomPlugins,
722
- resolvePluginPath,
723
- scanPluginNames
724
- }
9
+ const mod = require('./core/default');
10
+ module.exports = mod;