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,46 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
|
|
3
|
+
describe('ContextStorage', () => {
|
|
4
|
+
it('should provide request/session/agent context isolation', () => {
|
|
5
|
+
const storage = require('../../src/context/storage');
|
|
6
|
+
|
|
7
|
+
const results = [];
|
|
8
|
+
|
|
9
|
+
storage.runRequest({ req: 'A' }, () => {
|
|
10
|
+
results.push(storage.getRequest());
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
storage.runSession({ sess: 'B' }, () => {
|
|
14
|
+
results.push(storage.getSession());
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
storage.runAgent({ agent: 'C' }, () => {
|
|
18
|
+
results.push(storage.getAgent());
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
expect(results[0]).toEqual({ req: 'A' });
|
|
22
|
+
expect(results[1]).toEqual({ sess: 'B' });
|
|
23
|
+
expect(results[2]).toEqual({ agent: 'C' });
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it('should maintain isolation between nested contexts', () => {
|
|
27
|
+
const storage = require('../../src/context/storage');
|
|
28
|
+
|
|
29
|
+
const collected = [];
|
|
30
|
+
|
|
31
|
+
storage.runRequest({ id: 1 }, () => {
|
|
32
|
+
storage.runSession({ sid: 's1' }, () => {
|
|
33
|
+
collected.push({ req: storage.getRequest(), sess: storage.getSession() });
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
storage.runRequest({ id: 2 }, () => {
|
|
38
|
+
collected.push({ req: storage.getRequest(), sess: storage.getSession() });
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
expect(collected[0].req.id).toBe(1);
|
|
42
|
+
expect(collected[0].sess.sid).toBe('s1');
|
|
43
|
+
expect(collected[1].req.id).toBe(2);
|
|
44
|
+
expect(collected[1].sess).toBeUndefined();
|
|
45
|
+
});
|
|
46
|
+
});
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
|
|
3
|
+
describe('LLM Module', () => {
|
|
4
|
+
it('should export all expected symbols', () => {
|
|
5
|
+
const llm = require('../../src/llm');
|
|
6
|
+
expect(llm.createAI).toBeDefined();
|
|
7
|
+
expect(llm.createModel).toBeDefined();
|
|
8
|
+
expect(llm.getAvailableProviders).toBeDefined();
|
|
9
|
+
expect(llm.DEFAULT_PROVIDERS).toBeDefined();
|
|
10
|
+
expect(llm.isThinkingModel).toBeDefined();
|
|
11
|
+
expect(llm.ProviderRegistry).toBeDefined();
|
|
12
|
+
expect(llm.getProviderRegistry).toBeDefined();
|
|
13
|
+
expect(llm.TokenCounter).toBeDefined();
|
|
14
|
+
expect(llm.encode).toBeDefined();
|
|
15
|
+
expect(llm.estimateTokens).toBeDefined();
|
|
16
|
+
expect(llm.estimateContextTokens).toBeDefined();
|
|
17
|
+
expect(llm.shouldCompact).toBeDefined();
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it('should detect thinking models', () => {
|
|
21
|
+
const { isThinkingModel } = require('../../src/llm');
|
|
22
|
+
expect(isThinkingModel('deepseek-v4-pro')).toBe(true);
|
|
23
|
+
expect(isThinkingModel('deepseek-v4-flash')).toBe(true);
|
|
24
|
+
expect(isThinkingModel('deepseek-reasoner')).toBe(true);
|
|
25
|
+
expect(isThinkingModel('gpt-4o')).toBe(false);
|
|
26
|
+
expect(isThinkingModel(null)).toBe(false);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('should provide default provider list', () => {
|
|
30
|
+
const { getAvailableProviders, DEFAULT_PROVIDERS } = require('../../src/llm');
|
|
31
|
+
const providers = getAvailableProviders();
|
|
32
|
+
expect(Array.isArray(providers)).toBe(true);
|
|
33
|
+
expect(providers.length).toBeGreaterThan(0);
|
|
34
|
+
expect(DEFAULT_PROVIDERS.deepseek).toBeDefined();
|
|
35
|
+
expect(DEFAULT_PROVIDERS.anthropic).toBeDefined();
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('should have ProviderRegistry singleton', () => {
|
|
39
|
+
const { getProviderRegistry } = require('../../src/llm');
|
|
40
|
+
const reg1 = getProviderRegistry();
|
|
41
|
+
const reg2 = getProviderRegistry();
|
|
42
|
+
expect(reg1).toBe(reg2);
|
|
43
|
+
expect(reg1.getProviderNames().length).toBeGreaterThan(0);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it('should count tokens correctly', () => {
|
|
47
|
+
const { TokenCounter, encode } = require('../../src/llm');
|
|
48
|
+
const counter = new TokenCounter();
|
|
49
|
+
expect(typeof counter.countText).toBe('function');
|
|
50
|
+
expect(typeof counter.countMessages).toBe('function');
|
|
51
|
+
expect(encode('hello world')).toBeGreaterThan(0);
|
|
52
|
+
expect(encode('')).toBe(0);
|
|
53
|
+
});
|
|
54
|
+
});
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
|
|
3
|
+
describe('Plugin Module', () => {
|
|
4
|
+
it('should export all expected symbols', () => {
|
|
5
|
+
const plugin = require('../../src/plugin');
|
|
6
|
+
expect(plugin.Plugin).toBeDefined();
|
|
7
|
+
expect(plugin.PluginType).toBeDefined();
|
|
8
|
+
expect(plugin.PluginManager).toBeDefined();
|
|
9
|
+
expect(plugin.resolvePluginPath).toBeDefined();
|
|
10
|
+
expect(plugin.scanPluginNames).toBeDefined();
|
|
11
|
+
expect(plugin.loadPluginFromPath).toBeDefined();
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it('should have Plugin base class with default values', () => {
|
|
15
|
+
const { Plugin } = require('../../src/plugin');
|
|
16
|
+
const p = new Plugin();
|
|
17
|
+
expect(p.name).toBe('unnamed-plugin');
|
|
18
|
+
expect(p.version).toBe('1.0.0');
|
|
19
|
+
expect(p.priority).toBe(100);
|
|
20
|
+
expect(p.enabled).toBe(true);
|
|
21
|
+
expect(p.system).toBe(false);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('should have PluginType constants', () => {
|
|
25
|
+
const { PluginType } = require('../../src/plugin');
|
|
26
|
+
expect(PluginType.AI).toBe('ai');
|
|
27
|
+
expect(PluginType.TOOLS).toBe('tools');
|
|
28
|
+
expect(PluginType.EXECUTOR).toBe('executor');
|
|
29
|
+
expect(PluginType.CAPABILITY).toBe('capability');
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it('should create and configure PluginManager', () => {
|
|
33
|
+
const { PluginManager, PluginType } = require('../../src/plugin');
|
|
34
|
+
const framework = { getCwd: () => '/test', logger: { child: () => ({ debug: () => {}, info: () => {}, warn: () => {} }) } };
|
|
35
|
+
const pm = new PluginManager(framework);
|
|
36
|
+
expect(pm.count()).toBe(0);
|
|
37
|
+
|
|
38
|
+
const mockPlugin = { name: 'test', version: '1.0', enabled: true };
|
|
39
|
+
// PluginManager.register expects Plugin instance
|
|
40
|
+
expect(() => pm.register(mockPlugin)).toThrow('must be an instance of Plugin');
|
|
41
|
+
});
|
|
42
|
+
});
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
|
|
3
|
+
describe('Tool Module', () => {
|
|
4
|
+
it('should export all expected symbols', () => {
|
|
5
|
+
const tool = require('../../src/tool');
|
|
6
|
+
expect(tool.ToolRegistry).toBeDefined();
|
|
7
|
+
expect(tool.ToolExecutor).toBeDefined();
|
|
8
|
+
expect(tool.ToolRouter).toBeDefined();
|
|
9
|
+
expect(tool.jsonSchemaToZod).toBeDefined();
|
|
10
|
+
expect(tool.toAISDKTool).toBeDefined();
|
|
11
|
+
expect(tool.INTENT_PATTERNS).toBeDefined();
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it('should register and execute tools via ToolRegistry', async () => {
|
|
15
|
+
const { ToolRegistry } = require('../../src/tool');
|
|
16
|
+
const registry = new ToolRegistry();
|
|
17
|
+
|
|
18
|
+
const testTool = {
|
|
19
|
+
name: 'echo',
|
|
20
|
+
description: 'Echo back input',
|
|
21
|
+
execute: async (args) => ({ result: args.input }),
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
registry.register(testTool);
|
|
25
|
+
expect(registry.has('echo')).toBe(true);
|
|
26
|
+
expect(registry.get('echo').name).toBe('echo');
|
|
27
|
+
expect(registry.count()).toBe(1);
|
|
28
|
+
|
|
29
|
+
const result = await registry.execute('echo', { input: 'hello' });
|
|
30
|
+
expect(result.result).toBe('hello');
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('should throw on missing tool name', () => {
|
|
34
|
+
const { ToolRegistry } = require('../../src/tool');
|
|
35
|
+
const registry = new ToolRegistry();
|
|
36
|
+
expect(() => registry.register({ execute: async () => {} })).toThrow('Tool must have a name');
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('should get all tool names', () => {
|
|
40
|
+
const { ToolRegistry } = require('../../src/tool');
|
|
41
|
+
const registry = new ToolRegistry();
|
|
42
|
+
registry.register({ name: 'a', execute: async () => {} });
|
|
43
|
+
registry.register({ name: 'b', execute: async () => {} });
|
|
44
|
+
expect(registry.getNames()).toEqual(['a', 'b']);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('should clear all tools', () => {
|
|
48
|
+
const { ToolRegistry } = require('../../src/tool');
|
|
49
|
+
const registry = new ToolRegistry();
|
|
50
|
+
registry.register({ name: 'a', execute: async () => {} });
|
|
51
|
+
registry.clear();
|
|
52
|
+
expect(registry.count()).toBe(0);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it('should throw on tool not found', async () => {
|
|
56
|
+
const { ToolRegistry } = require('../../src/tool');
|
|
57
|
+
const registry = new ToolRegistry();
|
|
58
|
+
await expect(registry.execute('nonexistent', {})).rejects.toThrow('not found');
|
|
59
|
+
});
|
|
60
|
+
});
|
package/tests/setup.js
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
|
|
3
|
+
describe('Foliko Framework - Smoke Tests', () => {
|
|
4
|
+
it('should load main module exports', () => {
|
|
5
|
+
const foliko = require('../src/index');
|
|
6
|
+
expect(foliko.Framework).toBeDefined();
|
|
7
|
+
expect(foliko.Agent).toBeDefined();
|
|
8
|
+
expect(foliko.Plugin).toBeDefined();
|
|
9
|
+
expect(foliko.PluginManager).toBeDefined();
|
|
10
|
+
expect(foliko.ToolRegistry).toBeDefined();
|
|
11
|
+
expect(foliko.EventEmitter).toBeDefined();
|
|
12
|
+
expect(foliko.SkillManagerPlugin).toBeDefined();
|
|
13
|
+
expect(foliko.WorkflowPlugin).toBeDefined();
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it('should create a Framework instance', () => {
|
|
17
|
+
const { Framework } = require('../src/index');
|
|
18
|
+
const framework = new Framework();
|
|
19
|
+
expect(framework).toBeDefined();
|
|
20
|
+
expect(framework.constructor.name).toBe('Framework');
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it('should register and retrieve tools via ToolRegistry', () => {
|
|
24
|
+
const { ToolRegistry } = require('../src/index');
|
|
25
|
+
const registry = new ToolRegistry();
|
|
26
|
+
|
|
27
|
+
const testTool = {
|
|
28
|
+
name: 'test_tool',
|
|
29
|
+
description: 'A test tool',
|
|
30
|
+
execute: async () => ({ result: 'ok' }),
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
registry.register(testTool);
|
|
34
|
+
expect(registry.get('test_tool')).toBeDefined();
|
|
35
|
+
expect(registry.get('test_tool').name).toBe('test_tool');
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('should handle EventEmitter events', () => {
|
|
39
|
+
const { EventEmitter } = require('../src/index');
|
|
40
|
+
const emitter = new EventEmitter();
|
|
41
|
+
const events = [];
|
|
42
|
+
emitter.on('test', (msg) => events.push(msg));
|
|
43
|
+
emitter.emit('test', 'hello');
|
|
44
|
+
expect(events).toEqual(['hello']);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('should load framework utilities', () => {
|
|
48
|
+
const { logger } = require('../src/common/logger');
|
|
49
|
+
expect(logger).toBeDefined();
|
|
50
|
+
expect(typeof logger.info).toBe('function');
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('should load core constants', () => {
|
|
54
|
+
const constants = require('../src/common/constants');
|
|
55
|
+
expect(constants).toBeDefined();
|
|
56
|
+
expect(constants.PROMPT_PRIORITY).toBeDefined();
|
|
57
|
+
});
|
|
58
|
+
});
|
package/vitest.config.js
ADDED
package/cli/src/daemon.js
DELETED
|
@@ -1,149 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Foliko 后台服务入口
|
|
3
|
-
* 使用 framework.bootstrap() 自动加载 .foliko/ 目录配置
|
|
4
|
-
* 持续运行,不退出
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
const fs = require('fs');
|
|
8
|
-
const path = require('path');
|
|
9
|
-
|
|
10
|
-
// 默认配置
|
|
11
|
-
const DEFAULT_CONFIG = {
|
|
12
|
-
model: 'MiniMax-M2.7',
|
|
13
|
-
provider: 'minimax',
|
|
14
|
-
baseURL: 'https://api.minimaxi.com/v1',
|
|
15
|
-
apiKey: null,
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
// Provider 默认配置
|
|
19
|
-
const PROVIDER_DEFAULTS = {
|
|
20
|
-
minimax: {
|
|
21
|
-
model: 'MiniMax-M2.7',
|
|
22
|
-
baseURL: 'https://api.minimaxi.com/v1',
|
|
23
|
-
},
|
|
24
|
-
deepseek: {
|
|
25
|
-
model: 'deepseek-chat',
|
|
26
|
-
baseURL: 'https://api.deepseek.com/v1',
|
|
27
|
-
},
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
// 解析命令行参数 --cwd <path>
|
|
31
|
-
const cwdArg = process.argv.find((arg) => arg === '--cwd');
|
|
32
|
-
const daemonCwd = cwdArg ? process.argv[process.argv.indexOf(cwdArg) + 1] : process.cwd();
|
|
33
|
-
|
|
34
|
-
// 切换到工作目录
|
|
35
|
-
if (daemonCwd !== process.cwd()) {
|
|
36
|
-
process.chdir(daemonCwd);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
// 日志文件
|
|
40
|
-
const LOG_FILE = path.join(process.cwd(), '.foliko.log');
|
|
41
|
-
|
|
42
|
-
// 覆盖 console.log,同时写日志和终端
|
|
43
|
-
const originalLog = console.log;
|
|
44
|
-
const originalError = console.error;
|
|
45
|
-
console.log = (...args) => {
|
|
46
|
-
const msg = `[${new Date().toISOString()}] ` + args.join(' ');
|
|
47
|
-
fs.appendFileSync(LOG_FILE, msg + '\n');
|
|
48
|
-
originalLog.apply(console, args);
|
|
49
|
-
};
|
|
50
|
-
console.error = (...args) => {
|
|
51
|
-
const msg = `[${new Date().toISOString()}] ERROR ` + args.join(' ');
|
|
52
|
-
fs.appendFileSync(LOG_FILE, msg + '\n');
|
|
53
|
-
originalError.apply(console, args);
|
|
54
|
-
};
|
|
55
|
-
|
|
56
|
-
// 覆盖 process.stdout.write,只写日志(捕获框架日志)
|
|
57
|
-
const originalStdoutWrite = process.stdout.write.bind(process.stdout);
|
|
58
|
-
process.stdout.write = (chunk) => {
|
|
59
|
-
fs.appendFileSync(LOG_FILE, chunk.toString());
|
|
60
|
-
return originalStdoutWrite(chunk);
|
|
61
|
-
};
|
|
62
|
-
|
|
63
|
-
// 覆盖 process.stderr.write
|
|
64
|
-
const originalStderrWrite = process.stderr.write.bind(process.stderr);
|
|
65
|
-
process.stderr.write = (chunk) => {
|
|
66
|
-
fs.appendFileSync(LOG_FILE, chunk.toString());
|
|
67
|
-
return originalStderrWrite(chunk);
|
|
68
|
-
};
|
|
69
|
-
|
|
70
|
-
// 环境变量已在 commands/daemon.js 中通过 spawn 传递
|
|
71
|
-
const { Framework } = require('../../src');
|
|
72
|
-
|
|
73
|
-
// 调试:打印接收到的环境变量
|
|
74
|
-
console.log('[DEBUG] process.env.QQ_APP_ID =', process.env.QQ_APP_ID);
|
|
75
|
-
console.log('[DEBUG] process.env.QQ_CLIENT_SECRET =', process.env.QQ_CLIENT_SECRET ? '***' + process.env.QQ_CLIENT_SECRET.slice(-6) : 'undefined');
|
|
76
|
-
console.log('[DEBUG] process.env.FOLIKO_API_KEY =', process.env.FOLIKO_API_KEY ? '***' + process.env.FOLIKO_API_KEY.slice(-6) : 'undefined');
|
|
77
|
-
|
|
78
|
-
/**
|
|
79
|
-
* 获取环境变量配置
|
|
80
|
-
*/
|
|
81
|
-
function getEnvConfig() {
|
|
82
|
-
const provider = process.env.FOLIKO_PROVIDER || DEFAULT_CONFIG.provider;
|
|
83
|
-
const providerDefaults = PROVIDER_DEFAULTS[provider] || PROVIDER_DEFAULTS.minimax;
|
|
84
|
-
|
|
85
|
-
// 支持多种 API key 环境变量名
|
|
86
|
-
let apiKey = process.env.FOLIKO_API_KEY || null;
|
|
87
|
-
if (!apiKey) {
|
|
88
|
-
// 根据 provider 查找对应的 API key
|
|
89
|
-
const upperProvider = provider.toUpperCase().replace(/-/g, '_');
|
|
90
|
-
apiKey = process.env[`${upperProvider}_API_KEY`] || null;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
return {
|
|
94
|
-
model: process.env.FOLIKO_MODEL || providerDefaults.model,
|
|
95
|
-
provider: provider,
|
|
96
|
-
baseURL: process.env.FOLIKO_BASE_URL || providerDefaults.baseURL,
|
|
97
|
-
apiKey: apiKey,
|
|
98
|
-
};
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
/**
|
|
102
|
-
* 解析命令行参数(命令行参数优先于环境变量)
|
|
103
|
-
*/
|
|
104
|
-
function parseArgs(args) {
|
|
105
|
-
// 先获取环境变量配置(作为默认值)
|
|
106
|
-
const envConfig = getEnvConfig();
|
|
107
|
-
const options = { ...envConfig };
|
|
108
|
-
|
|
109
|
-
return options;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
async function main(args = process.argv.slice(2)) {
|
|
114
|
-
console.log('[Foliko] 启动后台服务...');
|
|
115
|
-
const options = parseArgs(args);
|
|
116
|
-
// 创建框架
|
|
117
|
-
const framework = new Framework({ debug: false });
|
|
118
|
-
|
|
119
|
-
// 使用 bootstrap 自动加载所有默认插件
|
|
120
|
-
await framework.bootstrap({
|
|
121
|
-
agentDir: './.foliko',
|
|
122
|
-
aiConfig: {
|
|
123
|
-
provider: options.provider,
|
|
124
|
-
model: options.model,
|
|
125
|
-
apiKey: options.apiKey,
|
|
126
|
-
baseURL: options.baseURL,
|
|
127
|
-
},
|
|
128
|
-
});
|
|
129
|
-
|
|
130
|
-
console.log('[Foliko] 框架已就绪');
|
|
131
|
-
console.log('[Foliko] 已加载插件:', framework.pluginManager.getAll().map((p) => p.name));
|
|
132
|
-
console.log('[Foliko] 后台服务运行中 (PID: ' + process.pid + ')');
|
|
133
|
-
|
|
134
|
-
// 优雅退出
|
|
135
|
-
const shutdown = async () => {
|
|
136
|
-
console.log('[Foliko] 正在关闭...');
|
|
137
|
-
await framework.destroy();
|
|
138
|
-
console.log('[Foliko] 已关闭');
|
|
139
|
-
process.exit(0);
|
|
140
|
-
};
|
|
141
|
-
|
|
142
|
-
process.on('SIGINT', shutdown);
|
|
143
|
-
process.on('SIGTERM', shutdown);
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
main().catch((err) => {
|
|
147
|
-
console.log('[Foliko] 启动失败:', err.message);
|
|
148
|
-
process.exit(1);
|
|
149
|
-
});
|