foliko 1.1.93 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (212) hide show
  1. package/.claude/settings.local.json +2 -1
  2. package/CLAUDE.md +56 -30
  3. package/REFACTORING_PLAN.md +645 -0
  4. package/docs/architecture.md +131 -0
  5. package/docs/migration.md +57 -0
  6. package/docs/public-api.md +138 -0
  7. package/docs/usage.md +385 -0
  8. package/examples/ambient-example.js +20 -137
  9. package/examples/basic.js +21 -48
  10. package/examples/bootstrap.js +16 -74
  11. package/examples/mcp-example.js +6 -29
  12. package/examples/skill-example.js +6 -19
  13. package/examples/workflow.js +8 -56
  14. package/package.json +8 -4
  15. package/plugins/README.md +49 -0
  16. package/plugins/{ambient-agent → ambient}/EventWatcher.js +1 -1
  17. package/plugins/{ambient-agent → ambient}/ExplorerLoop.js +3 -3
  18. package/plugins/{ambient-agent → ambient}/GoalManager.js +2 -2
  19. package/plugins/ambient/README.md +14 -0
  20. package/plugins/{ambient-agent → ambient}/Reflector.js +1 -1
  21. package/plugins/{ambient-agent → ambient}/StateStore.js +1 -1
  22. package/plugins/{ambient-agent → ambient}/index.js +2 -2
  23. package/plugins/{ai-plugin.js → core/ai/index.js} +14 -30
  24. package/plugins/{audit-plugin.js → core/audit/index.js} +3 -30
  25. package/plugins/{coordinator-plugin.js → core/coordinator/index.js} +3 -35
  26. package/plugins/core/default/bootstrap.js +224 -0
  27. package/plugins/core/default/config.js +222 -0
  28. package/plugins/core/default/index.js +58 -0
  29. package/plugins/core/mcp/index.js +1 -0
  30. package/plugins/{python-plugin-loader.js → core/python-loader/index.js} +7 -187
  31. package/plugins/{rules-plugin.js → core/rules/index.js} +121 -64
  32. package/plugins/{scheduler-plugin.js → core/scheduler/index.js} +12 -114
  33. package/plugins/{session-plugin.js → core/session/index.js} +9 -73
  34. package/{src/capabilities/skill-manager.js → plugins/core/skill-manager/index.js} +64 -18
  35. package/plugins/{storage-plugin.js → core/storage/index.js} +5 -29
  36. package/plugins/{subagent-plugin.js → core/sub-agent/index.js} +10 -171
  37. package/plugins/{think-plugin.js → core/think/index.js} +24 -91
  38. package/{src/capabilities/workflow-engine.js → plugins/core/workflow/index.js} +87 -85
  39. package/plugins/default-plugins.js +6 -720
  40. package/plugins/{data-splitter-plugin.js → executors/data-splitter/index.js} +9 -83
  41. package/plugins/{extension-executor-plugin.js → executors/extension/index.js} +13 -97
  42. package/plugins/{python-executor-plugin.js → executors/python/index.js} +6 -31
  43. package/plugins/{shell-executor-plugin.js → executors/shell/index.js} +2 -5
  44. package/plugins/install/README.md +9 -0
  45. package/plugins/{install-plugin.js → install/index.js} +3 -3
  46. package/plugins/{file-system-plugin.js → io/file-system/index.js} +34 -236
  47. package/plugins/{web-plugin.js → io/web/index.js} +11 -113
  48. package/plugins/memory/README.md +13 -0
  49. package/plugins/{memory-plugin.js → memory/index.js} +4 -18
  50. package/plugins/messaging/email/README.md +19 -0
  51. package/plugins/{email → messaging/email}/index.js +3 -3
  52. package/plugins/{feishu-plugin.js → messaging/feishu/index.js} +4 -4
  53. package/plugins/{qq-plugin.js → messaging/qq/index.js} +6 -17
  54. package/plugins/{telegram-plugin.js → messaging/telegram/index.js} +4 -4
  55. package/plugins/{weixin-plugin.js → messaging/weixin/index.js} +16 -16
  56. package/plugins/{plugin-manager-plugin.js → plugin-manager/index.js} +36 -180
  57. package/plugins/{tools-plugin.js → tools/index.js} +68 -116
  58. package/plugins/trading/README.md +15 -0
  59. package/plugins/{gate-trading.js → trading/index.js} +8 -8
  60. package/{examples → sandbox}/test-concurrent-chat.js +2 -2
  61. package/{examples → sandbox}/test-long-chat.js +2 -2
  62. package/{examples → sandbox}/test-session-chat.js +2 -2
  63. package/{examples → sandbox}/test-web-plugin.js +1 -1
  64. package/{examples → sandbox}/test-weixin-feishu.js +2 -2
  65. package/src/agent/base.js +56 -0
  66. package/src/{core/agent-chat.js → agent/chat.js} +11 -11
  67. package/src/{core/coordinator-manager.js → agent/coordinator.js} +3 -3
  68. package/src/agent/index.js +111 -0
  69. package/src/agent/main.js +337 -0
  70. package/src/agent/prompt.js +78 -0
  71. package/src/agent/sub.js +198 -0
  72. package/src/agent/worker.js +104 -0
  73. package/{cli/bin/foliko.js → src/cli/bin.js} +1 -1
  74. package/{cli/src → src/cli}/commands/chat.js +25 -21
  75. package/{cli/src → src/cli}/index.js +1 -0
  76. package/{cli/src → src/cli}/ui/chat-ui-old.js +40 -178
  77. package/{cli/src → src/cli}/ui/chat-ui.js +3 -3
  78. package/{cli/src → src/cli}/ui/components/footer-bar.js +1 -1
  79. package/src/common/errors.js +402 -0
  80. package/src/{utils → common}/logger.js +33 -0
  81. package/src/{utils/chat-queue.js → common/queue.js} +2 -2
  82. package/src/config/plugin-config.js +50 -0
  83. package/src/context/agent.js +32 -0
  84. package/src/context/compaction-prompts.js +170 -0
  85. package/src/context/compaction-utils.js +191 -0
  86. package/src/context/compressor.js +413 -0
  87. package/src/context/index.js +9 -0
  88. package/src/{core/context-manager.js → context/manager.js} +1 -1
  89. package/src/context/request.js +50 -0
  90. package/src/context/session.js +33 -0
  91. package/src/context/storage.js +30 -0
  92. package/src/executors/mcp-client.js +153 -0
  93. package/src/executors/mcp-desc.js +236 -0
  94. package/src/executors/mcp-executor.js +91 -956
  95. package/src/{core → framework}/command-registry.js +1 -1
  96. package/src/framework/framework.js +300 -0
  97. package/src/framework/index.js +18 -0
  98. package/src/framework/lifecycle.js +203 -0
  99. package/src/framework/loader.js +78 -0
  100. package/src/framework/registry.js +86 -0
  101. package/src/{core/ui-extension-context.js → framework/ui-extension.js} +1 -1
  102. package/src/index.js +130 -15
  103. package/src/llm/index.js +26 -0
  104. package/src/llm/provider.js +212 -0
  105. package/src/llm/registry.js +11 -0
  106. package/src/{core/token-counter.js → llm/tokens.js} +4 -37
  107. package/src/{core/plugin-base.js → plugin/base.js} +10 -136
  108. package/src/plugin/index.js +14 -0
  109. package/src/plugin/loader.js +101 -0
  110. package/src/plugin/manager.js +484 -0
  111. package/src/{core → session}/branch-summary-auto.js +2 -2
  112. package/src/{core/chat-session.js → session/chat.js} +2 -2
  113. package/src/session/index.js +7 -0
  114. package/src/{core/session-manager.js → session/session.js} +2 -2
  115. package/src/session/ttl.js +92 -0
  116. package/src/{core/jsonl-storage.js → storage/jsonl.js} +1 -1
  117. package/src/tool/executor.js +85 -0
  118. package/src/tool/index.js +15 -0
  119. package/src/tool/registry.js +143 -0
  120. package/src/{core/tool-router.js → tool/router.js} +17 -124
  121. package/src/tool/schema.js +108 -0
  122. package/src/utils/data-splitter.js +1 -1
  123. package/src/utils/download.js +1 -1
  124. package/src/utils/index.js +6 -6
  125. package/src/utils/message-validator.js +1 -1
  126. package/tests/core/context-storage.test.js +46 -0
  127. package/tests/core/llm.test.js +54 -0
  128. package/tests/core/plugin.test.js +42 -0
  129. package/tests/core/tool.test.js +60 -0
  130. package/tests/setup.js +10 -0
  131. package/tests/smoke.test.js +58 -0
  132. package/vitest.config.js +9 -0
  133. package/cli/src/daemon.js +0 -149
  134. package/docs/CONTEXT_DESIGN.md +0 -1596
  135. package/docs/ai-sdk-optimization.md +0 -655
  136. package/docs/features.md +0 -120
  137. package/docs/qq-bot.md +0 -976
  138. package/docs/quick-reference.md +0 -160
  139. package/docs/user-manual.md +0 -1391
  140. package/images/geometric_shapes.jpg +0 -0
  141. package/images/sunset_mountain_lake.jpg +0 -0
  142. package/skills/poster-guide/SKILL.md +0 -792
  143. package/src/capabilities/index.js +0 -11
  144. package/src/core/agent.js +0 -808
  145. package/src/core/context-compressor.js +0 -959
  146. package/src/core/enhanced-context-compressor.js +0 -210
  147. package/src/core/framework.js +0 -1422
  148. package/src/core/index.js +0 -30
  149. package/src/core/plugin-manager.js +0 -961
  150. package/src/core/provider-registry.js +0 -159
  151. package/src/core/provider.js +0 -156
  152. package/src/core/request-context.js +0 -98
  153. package/src/core/subagent.js +0 -442
  154. package/src/core/system-prompt-builder.js +0 -120
  155. package/src/core/tool-executor.js +0 -202
  156. package/src/core/tool-registry.js +0 -517
  157. package/src/core/worker-agent.js +0 -192
  158. package/src/executors/executor-base.js +0 -58
  159. package/src/utils/error-boundary.js +0 -363
  160. package/src/utils/error.js +0 -374
  161. package/system.md +0 -1645
  162. package/website_v2/README.md +0 -57
  163. package/website_v2/SPEC.md +0 -1
  164. package/website_v2/docs/api.html +0 -128
  165. package/website_v2/docs/configuration.html +0 -147
  166. package/website_v2/docs/plugin-development.html +0 -129
  167. package/website_v2/docs/project-structure.html +0 -89
  168. package/website_v2/docs/skill-development.html +0 -85
  169. package/website_v2/index.html +0 -489
  170. package/website_v2/scripts/main.js +0 -93
  171. package/website_v2/styles/animations.css +0 -8
  172. package/website_v2/styles/docs.css +0 -83
  173. package/website_v2/styles/main.css +0 -417
  174. package/xhs_auth.json +0 -268
  175. package//346/265/267/346/212/245/346/217/222/344/273/266.md +0 -621
  176. /package/plugins/{ambient-agent → ambient}/constants.js +0 -0
  177. /package/plugins/{email → messaging/email}/constants.js +0 -0
  178. /package/plugins/{email → messaging/email}/handlers.js +0 -0
  179. /package/plugins/{email → messaging/email}/monitor.js +0 -0
  180. /package/plugins/{email → messaging/email}/parser.js +0 -0
  181. /package/plugins/{email → messaging/email}/reply.js +0 -0
  182. /package/plugins/{email → messaging/email}/utils.js +0 -0
  183. /package/{examples → sandbox}/test-chat.js +0 -0
  184. /package/{examples → sandbox}/test-mcp.js +0 -0
  185. /package/{examples → sandbox}/test-reload.js +0 -0
  186. /package/{examples → sandbox}/test-telegram.js +0 -0
  187. /package/{examples → sandbox}/test-tg-bot.js +0 -0
  188. /package/{examples → sandbox}/test-tg-simple.js +0 -0
  189. /package/{examples → sandbox}/test-tg.js +0 -0
  190. /package/{examples → sandbox}/test-think.js +0 -0
  191. /package/src/{core/sub-agent-config.js → agent/sub-config.js} +0 -0
  192. /package/{cli/src → src/cli}/commands/daemon.js +0 -0
  193. /package/{cli/src → src/cli}/commands/list.js +0 -0
  194. /package/{cli/src → src/cli}/commands/plugin.js +0 -0
  195. /package/{cli/src → src/cli}/ui/components/agent-mention-provider.js +0 -0
  196. /package/{cli/src → src/cli}/ui/components/chained-autocomplete-provider.js +0 -0
  197. /package/{cli/src → src/cli}/ui/components/message-bubble.js +0 -0
  198. /package/{cli/src → src/cli}/ui/components/status-bar.js +0 -0
  199. /package/{cli/src → src/cli}/utils/ansi.js +0 -0
  200. /package/{cli/src → src/cli}/utils/config.js +0 -0
  201. /package/{cli/src → src/cli}/utils/markdown.js +0 -0
  202. /package/{cli/src → src/cli}/utils/plugin-config.js +0 -0
  203. /package/{cli/src → src/cli}/utils/render-diff.js +0 -0
  204. /package/src/{utils/circuit-breaker.js → common/circuit.js} +0 -0
  205. /package/src/{core → common}/constants.js +0 -0
  206. /package/src/{utils/edit-diff.js → common/diff.js} +0 -0
  207. /package/src/{utils/event-emitter.js → common/events.js} +0 -0
  208. /package/src/{utils → common}/id.js +0 -0
  209. /package/src/{utils → common}/retry.js +0 -0
  210. /package/src/{core/notification-manager.js → notification/manager.js} +0 -0
  211. /package/src/{core/session-entry.js → session/entry.js} +0 -0
  212. /package/src/{core/storage-manager.js → storage/manager.js} +0 -0
@@ -0,0 +1,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,10 @@
1
+ 'use strict';
2
+
3
+ // Test setup for Foliko
4
+ const path = require('path');
5
+
6
+ // Set test environment
7
+ process.env.NODE_ENV = 'test';
8
+
9
+ // Suppress logger output during tests
10
+ process.env.LOG_LEVEL = 'silent';
@@ -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
+ });
@@ -0,0 +1,9 @@
1
+ const { defineConfig } = require('vitest/config');
2
+
3
+ module.exports = defineConfig({
4
+ test: {
5
+ globals: true,
6
+ setupFiles: ['./tests/setup.js'],
7
+ include: ['tests/**/*.test.js'],
8
+ },
9
+ });
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
- });