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
@@ -0,0 +1,202 @@
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
+ const shouldLoad = (plugin) => {
69
+ const name = typeof plugin === 'string' ? plugin : (plugin.name || plugin.prototype?.name);
70
+ if (framework.pluginManager.has(name)) { return false; }
71
+ return true;
72
+ };
73
+
74
+ const aiConfig = { ...agentConfig.ai, ...config.aiConfig };
75
+ const upperProvider = (aiConfig.provider || 'deepseek').toUpperCase().replace(/-/g, '_');
76
+ const envApiKey = process.env[`${upperProvider}_API_KEY`] || process.env.FOLIKO_API_KEY;
77
+
78
+ // 0. Install plugin
79
+ if (shouldLoad('install')) {
80
+ const InstallPlugin = require('../../install');
81
+ await framework.loadPlugin(new InstallPlugin({ agentDir: agentConfig.agentDir }));
82
+ }
83
+
84
+ // 0.5 Tools plugin (built-in management tools)
85
+ if (shouldLoad('tools')) {
86
+ const ToolsPlugin = require('../../tools');
87
+ await framework.loadPlugin(new ToolsPlugin());
88
+ }
89
+
90
+ // 0.6 Plugin manager (remote plugin management)
91
+ if (shouldLoad('plugin-manager')) {
92
+ const PluginManagerPlugin = require('../../plugin-manager');
93
+ await framework.loadPlugin(new PluginManagerPlugin({ repo: agentConfig.pluginRepo }));
94
+ }
95
+
96
+ const skillsDirs = [...(config.skillsDirs || []), ...(agentConfig.skillsDirs || [])];
97
+
98
+ // 1. AI plugin
99
+ if (shouldLoad('ai') && (aiConfig.provider || aiConfig.model || aiConfig.apiKey || envApiKey)) {
100
+ const AIPlugin = require('../ai');
101
+ await framework.loadPlugin(new AIPlugin({
102
+ provider: aiConfig.provider || 'deepseek',
103
+ model: aiConfig.model || 'deepseek-chat',
104
+ apiKey: aiConfig.apiKey || envApiKey,
105
+ baseURL: aiConfig.baseURL,
106
+ }));
107
+ }
108
+
109
+ // 1.5 Main Agent
110
+ if (!framework._mainAgent) {
111
+ const { Agent } = require('../../../src/agent/main');
112
+ const aiPlugin = framework.pluginManager.get('ai');
113
+ framework._mainAgent = framework.createAgent({
114
+ name: 'MainAgent',
115
+ systemPrompt: '你是一个智能助手。当用户提出问题或任务时,你会主动分析需求,选择合适的工具来获取信息或执行操作。你善于将复杂任务拆解为多个步骤,通过工具协作完成。',
116
+ model: aiConfig.model || 'deepseek-chat',
117
+ provider: aiConfig.provider || 'deepseek',
118
+ apiKey: aiPlugin ? aiPlugin.config.apiKey : (aiConfig.apiKey || envApiKey),
119
+ baseURL: aiConfig.baseURL,
120
+ });
121
+ framework._agents.push(framework._mainAgent);
122
+ }
123
+
124
+ // 1.875 Extension executor (needed by skill-manager for slash commands)
125
+ if (shouldLoad('extension-executor')) {
126
+ const ExtensionExecutorPlugin = require('../../executors/extension');
127
+ await framework.loadPlugin(new ExtensionExecutorPlugin());
128
+ }
129
+
130
+ // 2. Skill manager
131
+ if (shouldLoad('skill-manager') && skillsDirs.length > 0) {
132
+ const { SkillManagerPlugin } = require('../skill-manager');
133
+ await framework.loadPlugin(new SkillManagerPlugin({ skillsDirs }));
134
+ }
135
+
136
+ // 3. MCP executor
137
+ if (shouldLoad('mcp')) {
138
+ const servers = Object.entries(agentConfig.mcpServers || {}).map(([name, cfg]) => ({
139
+ ...cfg, name, command: cfg.command, args: cfg.args || [], env: cfg.env || {},
140
+ url: cfg.url, headers: cfg.headers, enabled: cfg.enabled !== false,
141
+ }));
142
+ const { MCPExecutorPlugin } = require('../../../src/executors/mcp-executor');
143
+ await framework.loadPlugin(new MCPExecutorPlugin({ servers }));
144
+ }
145
+
146
+ // 4. Python plugin loader
147
+ if (shouldLoad('python-plugin-loader')) {
148
+ const PythonPluginLoader = require('../python-loader');
149
+ await framework.loadPlugin(new PythonPluginLoader({ agentDir: agentConfig.agentDir }));
150
+ }
151
+
152
+ // 5. SubAgent manager
153
+ if (shouldLoad('subagent-manager')) {
154
+ try {
155
+ const { SubAgentConfigManager } = require('../../../src/agent/sub-config');
156
+ const subAgentConfigManager = new SubAgentConfigManager(agentConfig.agentsDir);
157
+ subAgentConfigManager.loadAll();
158
+ framework._subAgentConfigManager = subAgentConfigManager;
159
+ const { SubAgentManagerPlugin } = require('../sub-agent');
160
+ await framework.loadPlugin(new SubAgentManagerPlugin({ agentsDir: agentConfig.agentsDir }));
161
+ } catch (err) { log.warn('SubAgent Manager failed:', err.message); }
162
+ }
163
+
164
+ // 6. Workflow engine
165
+ if (shouldLoad('workflow')) {
166
+ const { WorkflowPlugin } = require('../workflow');
167
+ await framework.loadPlugin(new WorkflowPlugin({
168
+ workflowsDir: agentConfig.workflowsDir || '.foliko/workflows',
169
+ }));
170
+ }
171
+
172
+ // 6.25 Rules engine
173
+ if (shouldLoad('rules')) {
174
+ const RulesPlugin = require('../rules');
175
+ await framework.loadPlugin(new RulesPlugin({
176
+ rulesDir: agentConfig.rulesDir || '.foliko/rules',
177
+ }));
178
+ }
179
+
180
+ // 6.5 Data splitter
181
+ if (shouldLoad('data-splitter')) {
182
+ const { DataSplitterPlugin } = require('../../executors/data-splitter');
183
+ await framework.loadPlugin(new DataSplitterPlugin({
184
+ autoSplitThreshold: agentConfig.dataSplitter?.autoSplitThreshold || 50000,
185
+ chunkSize: agentConfig.dataSplitter?.chunkSize || 60000,
186
+ maxConcurrent: agentConfig.dataSplitter?.maxConcurrent || 3,
187
+ }));
188
+ }
189
+
190
+ // 7. Custom plugins from directories
191
+ await loadCustomPlugins(framework, agentConfig);
192
+
193
+ // Start all
194
+ await framework.pluginManager.startAll();
195
+ }
196
+
197
+ module.exports = {
198
+ bootstrapDefaults,
199
+ loadCustomPlugins,
200
+ resolvePluginPath,
201
+ scanPluginNames,
202
+ };
@@ -0,0 +1,220 @@
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.email) config.email = pc.email;
93
+ if (pc.pluginLinks) config.pluginLinks = pc.pluginLinks;
94
+ } catch (err) { log.error('Failed to load plugins.json:', err.message); }
95
+ }
96
+
97
+ // load .foliko/weixin.json
98
+ const weixinFile = path.join(resolvedDir, 'weixin.json');
99
+ if (fs.existsSync(weixinFile)) {
100
+ try {
101
+ config.weixin = { ...config.weixin, ...JSON.parse(fs.readFileSync(weixinFile, 'utf-8')) };
102
+ } catch (err) { log.error('Failed to load weixin.json:', err.message); }
103
+ }
104
+
105
+ // load .foliko/mcp_config.json
106
+ const mcpFile = path.join(resolvedDir, 'mcp_config.json');
107
+ if (fs.existsSync(mcpFile)) {
108
+ try {
109
+ config.mcpServers = JSON.parse(fs.readFileSync(mcpFile, 'utf-8')).mcpServers || {};
110
+ } catch (err) { log.error('Failed to load mcp_config.json:', err.message); }
111
+ }
112
+
113
+ // skills dirs (顺序决定优先级:先加载的优先)
114
+ // 如果用户在 .foliko/config 配置了 skills_dirs,则用用户配置替换默认值
115
+ if (config.skillsDirsConfig && config.skillsDirsConfig.length > 0) {
116
+ config.skillsDirs = [...config.skillsDirsConfig];
117
+ } else {
118
+ // 默认路径
119
+ // 1. ~/.foliko/skills — 用户主目录下的全局技能(最高优先级)
120
+ // 1b. ~/.foliko/workflows — 用户全局工作流
121
+ const homeFoliko = path.join(os.homedir(), '.foliko');
122
+ const homeSkillsDir = path.join(homeFoliko, 'skills');
123
+ const homeWorkflowsDir = path.join(homeFoliko, 'workflows');
124
+ if (fs.existsSync(homeFoliko)) {
125
+ if (!fs.existsSync(homeSkillsDir)) fs.mkdirSync(homeSkillsDir, { recursive: true });
126
+ config.skillsDirs.push(homeSkillsDir);
127
+ if (!fs.existsSync(homeWorkflowsDir)) fs.mkdirSync(homeWorkflowsDir, { recursive: true });
128
+ config.skillsDirs.push(homeWorkflowsDir);
129
+ }
130
+
131
+ // 2. .foliko/skills — 项目级
132
+ const projectSkillsRel = path.join(agentDir, 'skills');
133
+ const projectSkillsAbs = path.resolve(cwd, projectSkillsRel);
134
+ if (fs.existsSync(resolvedDir) && !fs.existsSync(projectSkillsAbs)) {
135
+ fs.mkdirSync(projectSkillsAbs, { recursive: true });
136
+ }
137
+ if (fs.existsSync(projectSkillsAbs)) config.skillsDirs.push(projectSkillsRel);
138
+
139
+ // 3. .foliko/workflows/ — 工作流技能
140
+ const workflowsSkillsRel = path.join(agentDir, 'workflows');
141
+ const workflowsSkillsAbs = path.resolve(cwd, workflowsSkillsRel);
142
+ if (fs.existsSync(workflowsSkillsAbs)) config.skillsDirs.push(workflowsSkillsRel);
143
+
144
+ // 4. skills — 项目根目录
145
+ const cmdSkillsRel = 'skills';
146
+ const cmdSkillsAbs = path.resolve(cwd, cmdSkillsRel);
147
+ if (fs.existsSync(cmdSkillsAbs)) config.skillsDirs.push(cmdSkillsRel);
148
+
149
+ // 5. plugins/core/skills — 内置全局(最低优先级)
150
+ const rootSkillsDir = path.join(path.dirname(__dirname), 'skills');
151
+ if (fs.existsSync(rootSkillsDir)) config.skillsDirs.push(rootSkillsDir);
152
+ }
153
+
154
+ // Home dir (~/.foliko/) fallback
155
+ const homeDir = framework?.getHomeAgentDir?.();
156
+ const isHomeSame = homeDir && path.resolve(homeDir) === resolvedDir;
157
+ if (homeDir && fs.existsSync(homeDir) && !isHomeSame) {
158
+ const hc = path.join(homeDir, 'config');
159
+ if (!fs.existsSync(path.join(resolvedDir, 'config')) && fs.existsSync(hc)) {
160
+ try {
161
+ const lines = fs.readFileSync(hc, 'utf-8').split('\n');
162
+ for (const ln of lines) {
163
+ const t = ln.trim();
164
+ if (!t || t.startsWith('#')) continue;
165
+ const ci = t.indexOf(':');
166
+ if (ci === -1) continue;
167
+ const k = t.substring(0, ci).trim();
168
+ const v = t.substring(ci + 1).trim();
169
+ if ((k === 'ai_key' || k === 'api_key') && !config.ai.apiKey) config.ai.apiKey = v;
170
+ else if ((k === 'ai_model' || k === 'model') && !config.ai.model) config.ai.model = v;
171
+ else if ((k === 'ai_provider' || k === 'provider') && !config.ai.provider) config.ai.provider = v;
172
+ else if ((k === 'ai_base_url' || k === 'baseURL') && !config.ai.baseURL) config.ai.baseURL = v;
173
+ }
174
+ } catch (err) { log.error('Failed to load home config:', err.message); }
175
+ }
176
+
177
+ const homeAi = path.join(homeDir, 'ai.json');
178
+ if (!fs.existsSync(path.join(resolvedDir, 'ai.json')) && fs.existsSync(homeAi)) {
179
+ try { config.ai = { ...JSON.parse(fs.readFileSync(homeAi, 'utf-8')), ...config.ai }; } catch {}
180
+ }
181
+
182
+ const homePlugins = path.join(homeDir, 'plugins.json');
183
+ if (!fs.existsSync(path.join(resolvedDir, 'plugins.json')) && fs.existsSync(homePlugins)) {
184
+ 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.email && !config.email) config.email = pc.email; config.pluginLinks = { ...(pc.pluginLinks || {}), ...config.pluginLinks }; } catch {}
185
+ }
186
+
187
+ const homeMcp = path.join(homeDir, 'mcp_config.json');
188
+ if (!fs.existsSync(path.join(resolvedDir, 'mcp_config.json')) && fs.existsSync(homeMcp)) {
189
+ try { const m = JSON.parse(fs.readFileSync(homeMcp, 'utf-8')); config.mcpServers = { ...(m.mcpServers || {}), ...config.mcpServers }; } catch {}
190
+ }
191
+
192
+ const homeAgents = path.join(homeDir, 'agents');
193
+ if (!config.agentsDir && fs.existsSync(homeAgents)) config.agentsDir = homeAgents;
194
+
195
+ const homeSkills = path.join(homeDir, 'skills');
196
+ if (!fs.existsSync(homeSkills)) fs.mkdirSync(homeSkills, { recursive: true });
197
+ if (fs.existsSync(homeSkills)) {
198
+ const idx = config.skillsDirs.findIndex(d => d === path.join(resolvedDir, 'skills'));
199
+ config.skillsDirs.splice(idx >= 0 ? idx + 1 : config.skillsDirs.length, 0, homeSkills);
200
+ }
201
+
202
+ const homePluginsDir = path.join(homeDir, 'plugins');
203
+ if (fs.existsSync(homePluginsDir)) {
204
+ for (const entry of fs.readdirSync(homePluginsDir, { withFileTypes: true })) {
205
+ const name = entry.name.replace(/\.js$/, '');
206
+ const folderPath = path.join(homePluginsDir, name);
207
+ const filePath = path.join(homePluginsDir, entry.name);
208
+ if (entry.isDirectory()) {
209
+ if (fs.existsSync(path.join(folderPath, 'index.js'))) config.pluginLinks[name] = folderPath;
210
+ } else if (entry.isFile() && entry.name.endsWith('.js')) {
211
+ config.pluginLinks[name] = filePath;
212
+ }
213
+ }
214
+ }
215
+ }
216
+
217
+ return config;
218
+ }
219
+
220
+ 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');