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.
- package/.claude/settings.local.json +2 -1
- package/CLAUDE.md +56 -30
- package/REFACTORING_PLAN.md +645 -0
- package/docs/architecture.md +131 -0
- package/docs/migration.md +57 -0
- package/docs/public-api.md +138 -0
- package/docs/usage.md +385 -0
- package/examples/ambient-example.js +20 -137
- package/examples/basic.js +21 -48
- package/examples/bootstrap.js +16 -74
- package/examples/mcp-example.js +6 -29
- package/examples/skill-example.js +6 -19
- package/examples/workflow.js +8 -56
- package/package.json +8 -4
- package/plugins/README.md +49 -0
- package/plugins/{ambient-agent → ambient}/EventWatcher.js +1 -1
- package/plugins/{ambient-agent → ambient}/ExplorerLoop.js +3 -3
- package/plugins/{ambient-agent → ambient}/GoalManager.js +2 -2
- package/plugins/ambient/README.md +14 -0
- package/plugins/{ambient-agent → ambient}/Reflector.js +1 -1
- package/plugins/{ambient-agent → ambient}/StateStore.js +1 -1
- package/plugins/{ambient-agent → ambient}/index.js +2 -2
- package/plugins/{ai-plugin.js → core/ai/index.js} +14 -30
- package/plugins/{audit-plugin.js → core/audit/index.js} +3 -30
- package/plugins/{coordinator-plugin.js → core/coordinator/index.js} +3 -35
- package/plugins/core/default/bootstrap.js +224 -0
- package/plugins/core/default/config.js +222 -0
- package/plugins/core/default/index.js +58 -0
- package/plugins/core/mcp/index.js +1 -0
- package/plugins/{python-plugin-loader.js → core/python-loader/index.js} +7 -187
- package/plugins/{rules-plugin.js → core/rules/index.js} +121 -64
- package/plugins/{scheduler-plugin.js → core/scheduler/index.js} +12 -114
- package/plugins/{session-plugin.js → core/session/index.js} +9 -73
- package/{src/capabilities/skill-manager.js → plugins/core/skill-manager/index.js} +64 -18
- package/plugins/{storage-plugin.js → core/storage/index.js} +5 -29
- package/plugins/{subagent-plugin.js → core/sub-agent/index.js} +10 -171
- package/plugins/{think-plugin.js → core/think/index.js} +24 -91
- package/{src/capabilities/workflow-engine.js → plugins/core/workflow/index.js} +87 -85
- package/plugins/default-plugins.js +6 -720
- package/plugins/{data-splitter-plugin.js → executors/data-splitter/index.js} +9 -83
- package/plugins/{extension-executor-plugin.js → executors/extension/index.js} +13 -97
- package/plugins/{python-executor-plugin.js → executors/python/index.js} +6 -31
- package/plugins/{shell-executor-plugin.js → executors/shell/index.js} +2 -5
- package/plugins/install/README.md +9 -0
- package/plugins/{install-plugin.js → install/index.js} +3 -3
- package/plugins/{file-system-plugin.js → io/file-system/index.js} +34 -236
- package/plugins/{web-plugin.js → io/web/index.js} +11 -113
- package/plugins/memory/README.md +13 -0
- package/plugins/{memory-plugin.js → memory/index.js} +4 -18
- package/plugins/messaging/email/README.md +19 -0
- package/plugins/{email → messaging/email}/index.js +3 -3
- package/plugins/{feishu-plugin.js → messaging/feishu/index.js} +4 -4
- package/plugins/{qq-plugin.js → messaging/qq/index.js} +6 -17
- package/plugins/{telegram-plugin.js → messaging/telegram/index.js} +4 -4
- package/plugins/{weixin-plugin.js → messaging/weixin/index.js} +16 -16
- package/plugins/{plugin-manager-plugin.js → plugin-manager/index.js} +36 -180
- package/plugins/{tools-plugin.js → tools/index.js} +68 -116
- package/plugins/trading/README.md +15 -0
- package/plugins/{gate-trading.js → trading/index.js} +8 -8
- package/{examples → sandbox}/test-concurrent-chat.js +2 -2
- package/{examples → sandbox}/test-long-chat.js +2 -2
- package/{examples → sandbox}/test-session-chat.js +2 -2
- package/{examples → sandbox}/test-web-plugin.js +1 -1
- package/{examples → sandbox}/test-weixin-feishu.js +2 -2
- package/src/agent/base.js +56 -0
- package/src/{core/agent-chat.js → agent/chat.js} +11 -11
- package/src/{core/coordinator-manager.js → agent/coordinator.js} +3 -3
- package/src/agent/index.js +111 -0
- package/src/agent/main.js +337 -0
- package/src/agent/prompt.js +78 -0
- package/src/agent/sub.js +198 -0
- package/src/agent/worker.js +104 -0
- package/{cli/bin/foliko.js → src/cli/bin.js} +1 -1
- package/{cli/src → src/cli}/commands/chat.js +25 -21
- package/{cli/src → src/cli}/index.js +1 -0
- package/{cli/src → src/cli}/ui/chat-ui-old.js +40 -178
- package/{cli/src → src/cli}/ui/chat-ui.js +3 -3
- package/{cli/src → src/cli}/ui/components/footer-bar.js +1 -1
- package/src/common/errors.js +402 -0
- package/src/{utils → common}/logger.js +33 -0
- package/src/{utils/chat-queue.js → common/queue.js} +2 -2
- package/src/config/plugin-config.js +50 -0
- package/src/context/agent.js +32 -0
- package/src/context/compaction-prompts.js +170 -0
- package/src/context/compaction-utils.js +191 -0
- package/src/context/compressor.js +413 -0
- package/src/context/index.js +9 -0
- package/src/{core/context-manager.js → context/manager.js} +1 -1
- package/src/context/request.js +50 -0
- package/src/context/session.js +33 -0
- package/src/context/storage.js +30 -0
- package/src/executors/mcp-client.js +153 -0
- package/src/executors/mcp-desc.js +236 -0
- package/src/executors/mcp-executor.js +91 -956
- package/src/{core → framework}/command-registry.js +1 -1
- package/src/framework/framework.js +300 -0
- package/src/framework/index.js +18 -0
- package/src/framework/lifecycle.js +203 -0
- package/src/framework/loader.js +78 -0
- package/src/framework/registry.js +86 -0
- package/src/{core/ui-extension-context.js → framework/ui-extension.js} +1 -1
- package/src/index.js +130 -15
- package/src/llm/index.js +26 -0
- package/src/llm/provider.js +212 -0
- package/src/llm/registry.js +11 -0
- package/src/{core/token-counter.js → llm/tokens.js} +4 -37
- package/src/{core/plugin-base.js → plugin/base.js} +10 -136
- package/src/plugin/index.js +14 -0
- package/src/plugin/loader.js +101 -0
- package/src/plugin/manager.js +484 -0
- package/src/{core → session}/branch-summary-auto.js +2 -2
- package/src/{core/chat-session.js → session/chat.js} +2 -2
- package/src/session/index.js +7 -0
- package/src/{core/session-manager.js → session/session.js} +2 -2
- package/src/session/ttl.js +92 -0
- package/src/{core/jsonl-storage.js → storage/jsonl.js} +1 -1
- package/src/tool/executor.js +85 -0
- package/src/tool/index.js +15 -0
- package/src/tool/registry.js +143 -0
- package/src/{core/tool-router.js → tool/router.js} +17 -124
- package/src/tool/schema.js +108 -0
- package/src/utils/data-splitter.js +1 -1
- package/src/utils/download.js +1 -1
- package/src/utils/index.js +6 -6
- package/src/utils/message-validator.js +1 -1
- package/tests/core/context-storage.test.js +46 -0
- package/tests/core/llm.test.js +54 -0
- package/tests/core/plugin.test.js +42 -0
- package/tests/core/tool.test.js +60 -0
- package/tests/setup.js +10 -0
- package/tests/smoke.test.js +58 -0
- package/vitest.config.js +9 -0
- package/cli/src/daemon.js +0 -149
- package/docs/CONTEXT_DESIGN.md +0 -1596
- package/docs/ai-sdk-optimization.md +0 -655
- package/docs/features.md +0 -120
- package/docs/qq-bot.md +0 -976
- package/docs/quick-reference.md +0 -160
- package/docs/user-manual.md +0 -1391
- package/images/geometric_shapes.jpg +0 -0
- package/images/sunset_mountain_lake.jpg +0 -0
- package/skills/poster-guide/SKILL.md +0 -792
- package/src/capabilities/index.js +0 -11
- package/src/core/agent.js +0 -808
- package/src/core/context-compressor.js +0 -959
- package/src/core/enhanced-context-compressor.js +0 -210
- package/src/core/framework.js +0 -1422
- package/src/core/index.js +0 -30
- package/src/core/plugin-manager.js +0 -961
- package/src/core/provider-registry.js +0 -159
- package/src/core/provider.js +0 -156
- package/src/core/request-context.js +0 -98
- package/src/core/subagent.js +0 -442
- package/src/core/system-prompt-builder.js +0 -120
- package/src/core/tool-executor.js +0 -202
- package/src/core/tool-registry.js +0 -517
- package/src/core/worker-agent.js +0 -192
- package/src/executors/executor-base.js +0 -58
- package/src/utils/error-boundary.js +0 -363
- package/src/utils/error.js +0 -374
- package/system.md +0 -1645
- package/website_v2/README.md +0 -57
- package/website_v2/SPEC.md +0 -1
- package/website_v2/docs/api.html +0 -128
- package/website_v2/docs/configuration.html +0 -147
- package/website_v2/docs/plugin-development.html +0 -129
- package/website_v2/docs/project-structure.html +0 -89
- package/website_v2/docs/skill-development.html +0 -85
- package/website_v2/index.html +0 -489
- package/website_v2/scripts/main.js +0 -93
- package/website_v2/styles/animations.css +0 -8
- package/website_v2/styles/docs.css +0 -83
- package/website_v2/styles/main.css +0 -417
- package/xhs_auth.json +0 -268
- package//346/265/267/346/212/245/346/217/222/344/273/266.md +0 -621
- /package/plugins/{ambient-agent → ambient}/constants.js +0 -0
- /package/plugins/{email → messaging/email}/constants.js +0 -0
- /package/plugins/{email → messaging/email}/handlers.js +0 -0
- /package/plugins/{email → messaging/email}/monitor.js +0 -0
- /package/plugins/{email → messaging/email}/parser.js +0 -0
- /package/plugins/{email → messaging/email}/reply.js +0 -0
- /package/plugins/{email → messaging/email}/utils.js +0 -0
- /package/{examples → sandbox}/test-chat.js +0 -0
- /package/{examples → sandbox}/test-mcp.js +0 -0
- /package/{examples → sandbox}/test-reload.js +0 -0
- /package/{examples → sandbox}/test-telegram.js +0 -0
- /package/{examples → sandbox}/test-tg-bot.js +0 -0
- /package/{examples → sandbox}/test-tg-simple.js +0 -0
- /package/{examples → sandbox}/test-tg.js +0 -0
- /package/{examples → sandbox}/test-think.js +0 -0
- /package/src/{core/sub-agent-config.js → agent/sub-config.js} +0 -0
- /package/{cli/src → src/cli}/commands/daemon.js +0 -0
- /package/{cli/src → src/cli}/commands/list.js +0 -0
- /package/{cli/src → src/cli}/commands/plugin.js +0 -0
- /package/{cli/src → src/cli}/ui/components/agent-mention-provider.js +0 -0
- /package/{cli/src → src/cli}/ui/components/chained-autocomplete-provider.js +0 -0
- /package/{cli/src → src/cli}/ui/components/message-bubble.js +0 -0
- /package/{cli/src → src/cli}/ui/components/status-bar.js +0 -0
- /package/{cli/src → src/cli}/utils/ansi.js +0 -0
- /package/{cli/src → src/cli}/utils/config.js +0 -0
- /package/{cli/src → src/cli}/utils/markdown.js +0 -0
- /package/{cli/src → src/cli}/utils/plugin-config.js +0 -0
- /package/{cli/src → src/cli}/utils/render-diff.js +0 -0
- /package/src/{utils/circuit-breaker.js → common/circuit.js} +0 -0
- /package/src/{core → common}/constants.js +0 -0
- /package/src/{utils/edit-diff.js → common/diff.js} +0 -0
- /package/src/{utils/event-emitter.js → common/events.js} +0 -0
- /package/src/{utils → common}/id.js +0 -0
- /package/src/{utils → common}/retry.js +0 -0
- /package/src/{core/notification-manager.js → notification/manager.js} +0 -0
- /package/src/{core/session-entry.js → session/entry.js} +0 -0
- /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');
|