foliko 1.1.93 → 2.0.1

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 +224 -0
  27. package/plugins/core/default/config.js +222 -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 +3 -3
  52. package/plugins/{feishu-plugin.js → messaging/feishu/index.js} +4 -4
  53. package/plugins/{qq-plugin.js → messaging/qq/index.js} +6 -17
  54. package/plugins/{telegram-plugin.js → messaging/telegram/index.js} +4 -4
  55. package/plugins/{weixin-plugin.js → messaging/weixin/index.js} +16 -16
  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/common/errors.js +402 -0
  80. package/src/{utils → common}/logger.js +33 -0
  81. package/src/{utils/chat-queue.js → common/queue.js} +2 -2
  82. package/src/config/plugin-config.js +50 -0
  83. package/src/context/agent.js +32 -0
  84. package/src/context/compaction-prompts.js +170 -0
  85. package/src/context/compaction-utils.js +191 -0
  86. package/src/context/compressor.js +413 -0
  87. package/src/context/index.js +9 -0
  88. package/src/{core/context-manager.js → context/manager.js} +1 -1
  89. package/src/context/request.js +50 -0
  90. package/src/context/session.js +33 -0
  91. package/src/context/storage.js +30 -0
  92. package/src/executors/mcp-client.js +153 -0
  93. package/src/executors/mcp-desc.js +236 -0
  94. package/src/executors/mcp-executor.js +91 -956
  95. package/src/{core → framework}/command-registry.js +1 -1
  96. package/src/framework/framework.js +300 -0
  97. package/src/framework/index.js +18 -0
  98. package/src/framework/lifecycle.js +203 -0
  99. package/src/framework/loader.js +78 -0
  100. package/src/framework/registry.js +86 -0
  101. package/src/{core/ui-extension-context.js → framework/ui-extension.js} +1 -1
  102. package/src/index.js +130 -15
  103. package/src/llm/index.js +26 -0
  104. package/src/llm/provider.js +212 -0
  105. package/src/llm/registry.js +11 -0
  106. package/src/{core/token-counter.js → llm/tokens.js} +4 -37
  107. package/src/{core/plugin-base.js → plugin/base.js} +10 -136
  108. package/src/plugin/index.js +14 -0
  109. package/src/plugin/loader.js +101 -0
  110. package/src/plugin/manager.js +484 -0
  111. package/src/{core → session}/branch-summary-auto.js +2 -2
  112. package/src/{core/chat-session.js → session/chat.js} +2 -2
  113. package/src/session/index.js +7 -0
  114. package/src/{core/session-manager.js → session/session.js} +2 -2
  115. package/src/session/ttl.js +92 -0
  116. package/src/{core/jsonl-storage.js → storage/jsonl.js} +1 -1
  117. package/src/tool/executor.js +85 -0
  118. package/src/tool/index.js +15 -0
  119. package/src/tool/registry.js +143 -0
  120. package/src/{core/tool-router.js → tool/router.js} +17 -124
  121. package/src/tool/schema.js +108 -0
  122. package/src/utils/data-splitter.js +1 -1
  123. package/src/utils/download.js +1 -1
  124. package/src/utils/index.js +6 -6
  125. package/src/utils/message-validator.js +1 -1
  126. package/tests/core/context-storage.test.js +46 -0
  127. package/tests/core/llm.test.js +54 -0
  128. package/tests/core/plugin.test.js +42 -0
  129. package/tests/core/tool.test.js +60 -0
  130. package/tests/setup.js +10 -0
  131. package/tests/smoke.test.js +58 -0
  132. package/vitest.config.js +9 -0
  133. package/cli/src/daemon.js +0 -149
  134. package/docs/CONTEXT_DESIGN.md +0 -1596
  135. package/docs/ai-sdk-optimization.md +0 -655
  136. package/docs/features.md +0 -120
  137. package/docs/qq-bot.md +0 -976
  138. package/docs/quick-reference.md +0 -160
  139. package/docs/user-manual.md +0 -1391
  140. package/images/geometric_shapes.jpg +0 -0
  141. package/images/sunset_mountain_lake.jpg +0 -0
  142. package/skills/poster-guide/SKILL.md +0 -792
  143. package/src/capabilities/index.js +0 -11
  144. package/src/core/agent.js +0 -808
  145. package/src/core/context-compressor.js +0 -959
  146. package/src/core/enhanced-context-compressor.js +0 -210
  147. package/src/core/framework.js +0 -1422
  148. package/src/core/index.js +0 -30
  149. package/src/core/plugin-manager.js +0 -961
  150. package/src/core/provider-registry.js +0 -159
  151. package/src/core/provider.js +0 -156
  152. package/src/core/request-context.js +0 -98
  153. package/src/core/subagent.js +0 -442
  154. package/src/core/system-prompt-builder.js +0 -120
  155. package/src/core/tool-executor.js +0 -202
  156. package/src/core/tool-registry.js +0 -517
  157. package/src/core/worker-agent.js +0 -192
  158. package/src/executors/executor-base.js +0 -58
  159. package/src/utils/error-boundary.js +0 -363
  160. package/src/utils/error.js +0 -374
  161. package/system.md +0 -1645
  162. package/website_v2/README.md +0 -57
  163. package/website_v2/SPEC.md +0 -1
  164. package/website_v2/docs/api.html +0 -128
  165. package/website_v2/docs/configuration.html +0 -147
  166. package/website_v2/docs/plugin-development.html +0 -129
  167. package/website_v2/docs/project-structure.html +0 -89
  168. package/website_v2/docs/skill-development.html +0 -85
  169. package/website_v2/index.html +0 -489
  170. package/website_v2/scripts/main.js +0 -93
  171. package/website_v2/styles/animations.css +0 -8
  172. package/website_v2/styles/docs.css +0 -83
  173. package/website_v2/styles/main.css +0 -417
  174. package/xhs_auth.json +0 -268
  175. package//346/265/267/346/212/245/346/217/222/344/273/266.md +0 -621
  176. /package/plugins/{ambient-agent → ambient}/constants.js +0 -0
  177. /package/plugins/{email → messaging/email}/constants.js +0 -0
  178. /package/plugins/{email → messaging/email}/handlers.js +0 -0
  179. /package/plugins/{email → messaging/email}/monitor.js +0 -0
  180. /package/plugins/{email → messaging/email}/parser.js +0 -0
  181. /package/plugins/{email → messaging/email}/reply.js +0 -0
  182. /package/plugins/{email → messaging/email}/utils.js +0 -0
  183. /package/{examples → sandbox}/test-chat.js +0 -0
  184. /package/{examples → sandbox}/test-mcp.js +0 -0
  185. /package/{examples → sandbox}/test-reload.js +0 -0
  186. /package/{examples → sandbox}/test-telegram.js +0 -0
  187. /package/{examples → sandbox}/test-tg-bot.js +0 -0
  188. /package/{examples → sandbox}/test-tg-simple.js +0 -0
  189. /package/{examples → sandbox}/test-tg.js +0 -0
  190. /package/{examples → sandbox}/test-think.js +0 -0
  191. /package/src/{core/sub-agent-config.js → agent/sub-config.js} +0 -0
  192. /package/{cli/src → src/cli}/commands/daemon.js +0 -0
  193. /package/{cli/src → src/cli}/commands/list.js +0 -0
  194. /package/{cli/src → src/cli}/commands/plugin.js +0 -0
  195. /package/{cli/src → src/cli}/ui/components/agent-mention-provider.js +0 -0
  196. /package/{cli/src → src/cli}/ui/components/chained-autocomplete-provider.js +0 -0
  197. /package/{cli/src → src/cli}/ui/components/message-bubble.js +0 -0
  198. /package/{cli/src → src/cli}/ui/components/status-bar.js +0 -0
  199. /package/{cli/src → src/cli}/utils/ansi.js +0 -0
  200. /package/{cli/src → src/cli}/utils/config.js +0 -0
  201. /package/{cli/src → src/cli}/utils/markdown.js +0 -0
  202. /package/{cli/src → src/cli}/utils/plugin-config.js +0 -0
  203. /package/{cli/src → src/cli}/utils/render-diff.js +0 -0
  204. /package/src/{utils/circuit-breaker.js → common/circuit.js} +0 -0
  205. /package/src/{core → common}/constants.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
@@ -0,0 +1,224 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Bootstrap 辅助函数
5
+ * 根据配置加载所有默认插件
6
+ */
7
+
8
+ const { Plugin } = require('../../../src/plugin/base');
9
+ const { logger } = require('../../../src/common/logger');
10
+ const { resolvePluginPath, scanPluginNames } = require('../../../src/utils/plugin-helpers');
11
+ const path = require('path');
12
+ const fs = require('fs');
13
+ const log = logger.child('Bootstrap');
14
+
15
+ /**
16
+ * 加载自定义插件(.foliko/plugins/ 和项目 plugins/ 目录)
17
+ */
18
+ async function loadCustomPlugins(framework, agentConfig) {
19
+ const cwd = framework?.getCwd?.() ?? process.cwd();
20
+ const dirs = [
21
+ { dir: path.resolve(cwd, '.foliko', 'plugins'), forceEnabled: true },
22
+ { dir: path.resolve(cwd, 'plugins'), forceEnabled: false },
23
+ { dir: path.resolve(__dirname, '..', '..', '..', 'plugins'), forceEnabled: false },
24
+ ];
25
+ for (const { dir, forceEnabled } of dirs) {
26
+ if (!fs.existsSync(dir)) continue;
27
+ const names = scanPluginNames(dir);
28
+ for (const pluginName of names) {
29
+ try {
30
+ const resolved = resolvePluginPath(dir, pluginName);
31
+ if (!resolved) continue;
32
+ delete require.cache[require.resolve(resolved.path)];
33
+ const mod = require(resolved.path);
34
+ let pluginClass = mod.default || mod;
35
+ if (typeof pluginClass === 'function' && !(pluginClass.prototype instanceof Plugin)) {
36
+ const keys = Object.keys(mod).filter(k => mod[k] && mod[k].prototype instanceof Plugin);
37
+ if (keys.length > 0) pluginClass = mod[keys[0]];
38
+ }
39
+ let resolvedName = pluginName;
40
+ if (typeof pluginClass === 'function' && pluginClass.prototype instanceof Plugin) {
41
+ resolvedName = new pluginClass().name || pluginName;
42
+ }
43
+ if (framework.pluginManager.has(resolvedName) && framework.pluginManager.get(resolvedName)?._started) continue;
44
+ const pluginConfig = agentConfig[resolvedName] || {};
45
+ let instance;
46
+ if (typeof pluginClass === 'function' && pluginClass.prototype instanceof Plugin) {
47
+ instance = new pluginClass(pluginConfig);
48
+ } else {
49
+ instance = pluginClass;
50
+ }
51
+ await framework.pluginManager.load(instance, { forceEnabled });
52
+ } catch (err) { log.error(`Failed to load plugin ${pluginName}:`, err.message); }
53
+ }
54
+ }
55
+ }
56
+
57
+ /**
58
+ * Bootstrap 主函数
59
+ * 加载所有默认插件
60
+ */
61
+ async function bootstrapDefaults(framework, config = {}) {
62
+ const agentConfig = config._config;
63
+ if (!agentConfig) {
64
+ log.error('No config provided, skipping plugin loading');
65
+ return;
66
+ }
67
+
68
+ // 启用 bootstrap 模式:load() 只 install 不 start,由 startAll() 统一启动
69
+ framework.pluginManager.setBootstrapping(true);
70
+
71
+ const shouldLoad = (plugin) => {
72
+ const name = typeof plugin === 'string' ? plugin : (plugin.name || plugin.prototype?.name);
73
+ if (framework.pluginManager.has(name)) { return false; }
74
+ return true;
75
+ };
76
+
77
+ const aiConfig = { ...agentConfig.ai, ...config.aiConfig };
78
+ const upperProvider = (aiConfig.provider || 'deepseek').toUpperCase().replace(/-/g, '_');
79
+ const envApiKey = process.env[`${upperProvider}_API_KEY`] || process.env.FOLIKO_API_KEY;
80
+
81
+ // 0. Install plugin
82
+ if (shouldLoad('install')) {
83
+ const InstallPlugin = require('../../install');
84
+ await framework.loadPlugin(new InstallPlugin({ agentDir: agentConfig.agentDir }));
85
+ }
86
+
87
+ // 0.5 Tools plugin (built-in management tools)
88
+ if (shouldLoad('tools')) {
89
+ const ToolsPlugin = require('../../tools');
90
+ await framework.loadPlugin(new ToolsPlugin());
91
+ }
92
+
93
+ // 0.6 Plugin manager (remote plugin management)
94
+ if (shouldLoad('plugin-manager')) {
95
+ const PluginManagerPlugin = require('../../plugin-manager');
96
+ await framework.loadPlugin(new PluginManagerPlugin({ repo: agentConfig.pluginRepo }));
97
+ }
98
+
99
+ const skillsDirs = [...(config.skillsDirs || []), ...(agentConfig.skillsDirs || [])];
100
+
101
+ // 1. AI plugin
102
+ if (shouldLoad('ai') && (aiConfig.provider || aiConfig.model || aiConfig.apiKey || envApiKey)) {
103
+ const AIPlugin = require('../ai');
104
+ await framework.loadPlugin(new AIPlugin({
105
+ provider: aiConfig.provider || 'deepseek',
106
+ model: aiConfig.model || 'deepseek-chat',
107
+ apiKey: aiConfig.apiKey || envApiKey,
108
+ baseURL: aiConfig.baseURL,
109
+ }));
110
+ }
111
+
112
+ // 1.5 Main Agent
113
+ if (!framework._mainAgent) {
114
+ const { Agent } = require('../../../src/agent/main');
115
+ const aiPlugin = framework.pluginManager.get('ai');
116
+ framework._mainAgent = framework.createAgent({
117
+ name: 'MainAgent',
118
+ systemPrompt: '你是一个智能助手。当用户提出问题或任务时,你会主动分析需求,选择合适的工具来获取信息或执行操作。你善于将复杂任务拆解为多个步骤,通过工具协作完成。',
119
+ model: aiConfig.model || 'deepseek-chat',
120
+ provider: aiConfig.provider || 'deepseek',
121
+ apiKey: aiPlugin ? aiPlugin.config.apiKey : (aiConfig.apiKey || envApiKey),
122
+ baseURL: aiConfig.baseURL,
123
+ });
124
+ framework._agents.push(framework._mainAgent);
125
+ }
126
+
127
+ // 1.875 Extension executor (needed by skill-manager for slash commands)
128
+ if (shouldLoad('extension-executor')) {
129
+ const ExtensionExecutorPlugin = require('../../executors/extension');
130
+ await framework.loadPlugin(new ExtensionExecutorPlugin());
131
+ }
132
+
133
+ // 2. Skill manager
134
+ if (shouldLoad('skill-manager') && skillsDirs.length > 0) {
135
+ const { SkillManagerPlugin } = require('../skill-manager');
136
+ await framework.loadPlugin(new SkillManagerPlugin({ skillsDirs }));
137
+ }
138
+
139
+ // 3. MCP executor
140
+ if (shouldLoad('mcp')) {
141
+ const servers = Object.entries(agentConfig.mcpServers || {}).map(([name, cfg]) => ({
142
+ ...cfg, name, command: cfg.command, args: cfg.args || [], env: cfg.env || {},
143
+ url: cfg.url, headers: cfg.headers, enabled: cfg.enabled !== false,
144
+ }));
145
+ const { MCPExecutorPlugin } = require('../../../src/executors/mcp-executor');
146
+ await framework.loadPlugin(new MCPExecutorPlugin({ servers }));
147
+ }
148
+
149
+ // 4. Python plugin loader
150
+ if (shouldLoad('python-plugin-loader')) {
151
+ const PythonPluginLoader = require('../python-loader');
152
+ await framework.loadPlugin(new PythonPluginLoader({ agentDir: agentConfig.agentDir }));
153
+ }
154
+
155
+ // 5. SubAgent manager
156
+ if (shouldLoad('subagent-manager')) {
157
+ try {
158
+ const { SubAgentConfigManager } = require('../../../src/agent/sub-config');
159
+ const subAgentConfigManager = new SubAgentConfigManager(agentConfig.agentsDir);
160
+ subAgentConfigManager.loadAll();
161
+ framework._subAgentConfigManager = subAgentConfigManager;
162
+ const { SubAgentManagerPlugin } = require('../sub-agent');
163
+ await framework.loadPlugin(new SubAgentManagerPlugin({ agentsDir: agentConfig.agentsDir }));
164
+ } catch (err) { log.warn('SubAgent Manager failed:', err.message); }
165
+ }
166
+
167
+ // 6. Workflow engine
168
+ if (shouldLoad('workflow')) {
169
+ const { WorkflowPlugin } = require('../workflow');
170
+ await framework.loadPlugin(new WorkflowPlugin({
171
+ workflowsDir: agentConfig.workflowsDir || '.foliko/workflows',
172
+ }));
173
+ }
174
+
175
+ // 6.25 Rules engine
176
+ if (shouldLoad('rules')) {
177
+ const RulesPlugin = require('../rules');
178
+ await framework.loadPlugin(new RulesPlugin({
179
+ rulesDir: agentConfig.rulesDir || '.foliko/rules',
180
+ }));
181
+ }
182
+
183
+ // 6.5 Data splitter
184
+ if (shouldLoad('data-splitter')) {
185
+ const { DataSplitterPlugin } = require('../../executors/data-splitter');
186
+ await framework.loadPlugin(new DataSplitterPlugin({
187
+ autoSplitThreshold: agentConfig.dataSplitter?.autoSplitThreshold || 50000,
188
+ chunkSize: agentConfig.dataSplitter?.chunkSize || 60000,
189
+ maxConcurrent: agentConfig.dataSplitter?.maxConcurrent || 3,
190
+ }));
191
+ }
192
+
193
+ // 6.75 Messaging plugins (from plugins/messaging/ subdirectory)
194
+ const messagingPlugins = ['telegram', 'qq', 'weixin', 'feishu', 'email'];
195
+ for (const name of messagingPlugins) {
196
+ if (shouldLoad(name)) {
197
+ try {
198
+ const pluginDir = path.resolve(__dirname, '..', '..', '..', 'plugins', 'messaging', name);
199
+ if (!fs.existsSync(pluginDir)) continue;
200
+ const PluginClass = require(`../../messaging/${name}`);
201
+ const pluginConfig = agentConfig[name] || {};
202
+ await framework.loadPlugin(new PluginClass(pluginConfig));
203
+ } catch (err) {
204
+ log.warn(`Messaging plugin '${name}' failed to load:`, err.message);
205
+ }
206
+ }
207
+ }
208
+
209
+ // 7. Custom plugins from directories
210
+ await loadCustomPlugins(framework, agentConfig);
211
+
212
+ // 统一启动所有插件
213
+ await framework.pluginManager.startAll();
214
+
215
+ // 退出 bootstrap 模式
216
+ framework.pluginManager.setBootstrapping(false);
217
+ }
218
+
219
+ module.exports = {
220
+ bootstrapDefaults,
221
+ loadCustomPlugins,
222
+ resolvePluginPath,
223
+ scanPluginNames,
224
+ };
@@ -0,0 +1,222 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Agent 配置加载器
5
+ * 检测 .foliko/ 目录下的配置,提供给 bootstrap() 使用
6
+ */
7
+
8
+ const fs = require('fs');
9
+ const path = require('path');
10
+ const os = require('os');
11
+ const { logger } = require('../../../src/common/logger');
12
+ const log = logger.child('AgentConfig');
13
+
14
+ /**
15
+ * 加载 .foliko 目录下的配置
16
+ * @returns {Object} 配置对象
17
+ */
18
+ function loadAgentConfig(framework, agentDir = '.foliko') {
19
+ if (typeof framework === 'string') {
20
+ agentDir = framework;
21
+ framework = null;
22
+ }
23
+
24
+ const fwCwd = framework?.getCwd?.() ?? process.cwd();
25
+ if (path.isAbsolute(agentDir)) {
26
+ const rel = path.relative(fwCwd, agentDir);
27
+ if (rel && !rel.startsWith('..') && !path.isAbsolute(rel)) {
28
+ agentDir = rel.split(path.sep).join('/');
29
+ }
30
+ }
31
+
32
+ const config = {
33
+ agentDir,
34
+ ai: {},
35
+ mcpServers: {},
36
+ plugins: [],
37
+ skillsDirs: [],
38
+ agentsDir: null,
39
+ pluginLinks: {},
40
+ };
41
+ const cwd = framework?.getCwd?.() ?? process.cwd();
42
+ const resolvedDir = path.resolve(cwd, agentDir);
43
+
44
+ const agentsDir = path.join(resolvedDir, 'agents');
45
+ if (fs.existsSync(agentsDir)) config.agentsDir = agentsDir;
46
+
47
+ // load .foliko/config
48
+ const configFile = path.join(resolvedDir, 'config');
49
+ if (fs.existsSync(configFile)) {
50
+ try {
51
+ const lines = fs.readFileSync(configFile, 'utf-8').split('\n');
52
+ for (const line of lines) {
53
+ const t = line.trim();
54
+ if (!t || t.startsWith('#')) continue;
55
+ const ci = t.indexOf(':');
56
+ if (ci === -1) continue;
57
+ const key = t.substring(0, ci).trim();
58
+ const val = t.substring(ci + 1).trim();
59
+ if (key === 'ai_key' || key === 'api_key') config.ai.apiKey = val;
60
+ else if (key === 'ai_model' || key === 'model') config.ai.model = val;
61
+ else if (key === 'ai_provider' || key === 'provider') config.ai.provider = val;
62
+ else if (key === 'ai_base_url' || key === 'baseURL') config.ai.baseURL = val;
63
+ // 目录路径配置(覆盖默认值)
64
+ else if (key === 'skills_dirs' || key === 'skillsDirs') {
65
+ config.skillsDirsConfig = val.split(',').map(s => s.trim()).filter(Boolean);
66
+ } else if (key === 'workflows_dir' || key === 'workflowsDir') {
67
+ config.workflowsDir = val;
68
+ } else if (key === 'rules_dir' || key === 'rulesDir') {
69
+ config.rulesDir = val;
70
+ } else if (key === 'agents_dir' || key === 'agentsDir') {
71
+ config.agentsDir = val;
72
+ }
73
+ }
74
+ } catch (err) { log.error('Failed to load config:', err.message); }
75
+ }
76
+
77
+ // load .foliko/ai.json
78
+ const aiFile = path.join(resolvedDir, 'ai.json');
79
+ if (fs.existsSync(aiFile)) {
80
+ try {
81
+ config.ai = { ...config.ai, ...JSON.parse(fs.readFileSync(aiFile, 'utf-8')) };
82
+ } catch (err) { log.error('Failed to load ai.json:', err.message); }
83
+ }
84
+
85
+ // load .foliko/plugins.json
86
+ const pluginsFile = path.join(resolvedDir, 'plugins.json');
87
+ if (fs.existsSync(pluginsFile)) {
88
+ try {
89
+ const pc = JSON.parse(fs.readFileSync(pluginsFile, 'utf-8'));
90
+ if (pc.telegram) config.telegram = pc.telegram;
91
+ if (pc.weixin) config.weixin = pc.weixin;
92
+ if (pc.qq) config.qq = pc.qq;
93
+ if (pc.feishu) config.feishu = pc.feishu;
94
+ if (pc.email) config.email = pc.email;
95
+ if (pc.pluginLinks) config.pluginLinks = pc.pluginLinks;
96
+ } catch (err) { log.error('Failed to load plugins.json:', err.message); }
97
+ }
98
+
99
+ // load .foliko/weixin.json
100
+ const weixinFile = path.join(resolvedDir, 'weixin.json');
101
+ if (fs.existsSync(weixinFile)) {
102
+ try {
103
+ config.weixin = { ...config.weixin, ...JSON.parse(fs.readFileSync(weixinFile, 'utf-8')) };
104
+ } catch (err) { log.error('Failed to load weixin.json:', err.message); }
105
+ }
106
+
107
+ // load .foliko/mcp_config.json
108
+ const mcpFile = path.join(resolvedDir, 'mcp_config.json');
109
+ if (fs.existsSync(mcpFile)) {
110
+ try {
111
+ config.mcpServers = JSON.parse(fs.readFileSync(mcpFile, 'utf-8')).mcpServers || {};
112
+ } catch (err) { log.error('Failed to load mcp_config.json:', err.message); }
113
+ }
114
+
115
+ // skills dirs (顺序决定优先级:先加载的优先)
116
+ // 如果用户在 .foliko/config 配置了 skills_dirs,则用用户配置替换默认值
117
+ if (config.skillsDirsConfig && config.skillsDirsConfig.length > 0) {
118
+ config.skillsDirs = [...config.skillsDirsConfig];
119
+ } else {
120
+ // 默认路径
121
+ // 1. ~/.foliko/skills — 用户主目录下的全局技能(最高优先级)
122
+ // 1b. ~/.foliko/workflows — 用户全局工作流
123
+ const homeFoliko = path.join(os.homedir(), '.foliko');
124
+ const homeSkillsDir = path.join(homeFoliko, 'skills');
125
+ const homeWorkflowsDir = path.join(homeFoliko, 'workflows');
126
+ if (fs.existsSync(homeFoliko)) {
127
+ if (!fs.existsSync(homeSkillsDir)) fs.mkdirSync(homeSkillsDir, { recursive: true });
128
+ config.skillsDirs.push(homeSkillsDir);
129
+ if (!fs.existsSync(homeWorkflowsDir)) fs.mkdirSync(homeWorkflowsDir, { recursive: true });
130
+ config.skillsDirs.push(homeWorkflowsDir);
131
+ }
132
+
133
+ // 2. .foliko/skills — 项目级
134
+ const projectSkillsRel = path.join(agentDir, 'skills');
135
+ const projectSkillsAbs = path.resolve(cwd, projectSkillsRel);
136
+ if (fs.existsSync(resolvedDir) && !fs.existsSync(projectSkillsAbs)) {
137
+ fs.mkdirSync(projectSkillsAbs, { recursive: true });
138
+ }
139
+ if (fs.existsSync(projectSkillsAbs)) config.skillsDirs.push(projectSkillsRel);
140
+
141
+ // 3. .foliko/workflows/ — 工作流技能
142
+ const workflowsSkillsRel = path.join(agentDir, 'workflows');
143
+ const workflowsSkillsAbs = path.resolve(cwd, workflowsSkillsRel);
144
+ if (fs.existsSync(workflowsSkillsAbs)) config.skillsDirs.push(workflowsSkillsRel);
145
+
146
+ // 4. skills — 项目根目录
147
+ const cmdSkillsRel = 'skills';
148
+ const cmdSkillsAbs = path.resolve(cwd, cmdSkillsRel);
149
+ if (fs.existsSync(cmdSkillsAbs)) config.skillsDirs.push(cmdSkillsRel);
150
+
151
+ // 5. plugins/core/skills — 内置全局(最低优先级)
152
+ const rootSkillsDir = path.join(path.dirname(__dirname), 'skills');
153
+ if (fs.existsSync(rootSkillsDir)) config.skillsDirs.push(rootSkillsDir);
154
+ }
155
+
156
+ // Home dir (~/.foliko/) fallback
157
+ const homeDir = framework?.getHomeAgentDir?.();
158
+ const isHomeSame = homeDir && path.resolve(homeDir) === resolvedDir;
159
+ if (homeDir && fs.existsSync(homeDir) && !isHomeSame) {
160
+ const hc = path.join(homeDir, 'config');
161
+ if (!fs.existsSync(path.join(resolvedDir, 'config')) && fs.existsSync(hc)) {
162
+ try {
163
+ const lines = fs.readFileSync(hc, 'utf-8').split('\n');
164
+ for (const ln of lines) {
165
+ const t = ln.trim();
166
+ if (!t || t.startsWith('#')) continue;
167
+ const ci = t.indexOf(':');
168
+ if (ci === -1) continue;
169
+ const k = t.substring(0, ci).trim();
170
+ const v = t.substring(ci + 1).trim();
171
+ if ((k === 'ai_key' || k === 'api_key') && !config.ai.apiKey) config.ai.apiKey = v;
172
+ else if ((k === 'ai_model' || k === 'model') && !config.ai.model) config.ai.model = v;
173
+ else if ((k === 'ai_provider' || k === 'provider') && !config.ai.provider) config.ai.provider = v;
174
+ else if ((k === 'ai_base_url' || k === 'baseURL') && !config.ai.baseURL) config.ai.baseURL = v;
175
+ }
176
+ } catch (err) { log.error('Failed to load home config:', err.message); }
177
+ }
178
+
179
+ const homeAi = path.join(homeDir, 'ai.json');
180
+ if (!fs.existsSync(path.join(resolvedDir, 'ai.json')) && fs.existsSync(homeAi)) {
181
+ try { config.ai = { ...JSON.parse(fs.readFileSync(homeAi, 'utf-8')), ...config.ai }; } catch {}
182
+ }
183
+
184
+ const homePlugins = path.join(homeDir, 'plugins.json');
185
+ if (!fs.existsSync(path.join(resolvedDir, 'plugins.json')) && fs.existsSync(homePlugins)) {
186
+ try { const pc = JSON.parse(fs.readFileSync(homePlugins, 'utf-8')); if (pc.telegram && !config.telegram) config.telegram = pc.telegram; if (pc.weixin && !config.weixin) config.weixin = pc.weixin; if (pc.qq && !config.qq) config.qq = pc.qq; if (pc.feishu && !config.feishu) config.feishu = pc.feishu; if (pc.email && !config.email) config.email = pc.email; config.pluginLinks = { ...(pc.pluginLinks || {}), ...config.pluginLinks }; } catch {}
187
+ }
188
+
189
+ const homeMcp = path.join(homeDir, 'mcp_config.json');
190
+ if (!fs.existsSync(path.join(resolvedDir, 'mcp_config.json')) && fs.existsSync(homeMcp)) {
191
+ try { const m = JSON.parse(fs.readFileSync(homeMcp, 'utf-8')); config.mcpServers = { ...(m.mcpServers || {}), ...config.mcpServers }; } catch {}
192
+ }
193
+
194
+ const homeAgents = path.join(homeDir, 'agents');
195
+ if (!config.agentsDir && fs.existsSync(homeAgents)) config.agentsDir = homeAgents;
196
+
197
+ const homeSkills = path.join(homeDir, 'skills');
198
+ if (!fs.existsSync(homeSkills)) fs.mkdirSync(homeSkills, { recursive: true });
199
+ if (fs.existsSync(homeSkills)) {
200
+ const idx = config.skillsDirs.findIndex(d => d === path.join(resolvedDir, 'skills'));
201
+ config.skillsDirs.splice(idx >= 0 ? idx + 1 : config.skillsDirs.length, 0, homeSkills);
202
+ }
203
+
204
+ const homePluginsDir = path.join(homeDir, 'plugins');
205
+ if (fs.existsSync(homePluginsDir)) {
206
+ for (const entry of fs.readdirSync(homePluginsDir, { withFileTypes: true })) {
207
+ const name = entry.name.replace(/\.js$/, '');
208
+ const folderPath = path.join(homePluginsDir, name);
209
+ const filePath = path.join(homePluginsDir, entry.name);
210
+ if (entry.isDirectory()) {
211
+ if (fs.existsSync(path.join(folderPath, 'index.js'))) config.pluginLinks[name] = folderPath;
212
+ } else if (entry.isFile() && entry.name.endsWith('.js')) {
213
+ config.pluginLinks[name] = filePath;
214
+ }
215
+ }
216
+ }
217
+ }
218
+
219
+ return config;
220
+ }
221
+
222
+ module.exports = { loadAgentConfig };
@@ -0,0 +1,58 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * DefaultPlugins — 默认插件配置加载器
5
+ * 组合 config.js + bootstrap.js
6
+ */
7
+
8
+ const { Plugin } = require('../../../src/plugin/base');
9
+ const { loadAgentConfig } = require('./config');
10
+ const { bootstrapDefaults, loadCustomPlugins, resolvePluginPath, scanPluginNames } = require('./bootstrap');
11
+
12
+ class DefaultPlugins extends Plugin {
13
+ constructor(config = {}) {
14
+ super();
15
+ this.name = 'defaults';
16
+ this.version = '1.0.0';
17
+ this.description = '默认插件配置加载器';
18
+ this.priority = 0;
19
+ this.system = true;
20
+
21
+ this._framework = null;
22
+ this._agentDir = config.agentDir || '.foliko';
23
+ this._config = null;
24
+ }
25
+
26
+ install(framework) {
27
+ this._framework = framework;
28
+ return this;
29
+ }
30
+
31
+ start(framework) {
32
+ this._config = loadAgentConfig(this._framework, this._agentDir);
33
+ return this;
34
+ }
35
+
36
+ getConfig() {
37
+ return this._config || loadAgentConfig(this._framework, this._agentDir);
38
+ }
39
+
40
+ reload(framework) {
41
+ this._framework = framework;
42
+ this._config = loadAgentConfig(this._framework, this._agentDir);
43
+ }
44
+
45
+ uninstall(framework) {
46
+ this._framework = null;
47
+ this._config = null;
48
+ }
49
+ }
50
+
51
+ module.exports = {
52
+ DefaultPlugins,
53
+ loadAgentConfig,
54
+ bootstrapDefaults,
55
+ loadCustomPlugins,
56
+ resolvePluginPath,
57
+ scanPluginNames,
58
+ };
@@ -0,0 +1 @@
1
+ module.exports = require('../../../src/executors/mcp-executor');