foliko 1.0.48 → 1.0.50

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.
@@ -116,7 +116,8 @@
116
116
  "Bash(taskkill //F //PID 19848)",
117
117
  "Bash(curl -s -X POST \"http://localhost:3000/webhook/075s5s2umn4smn4f\" \\\\\n -H \"Content-Type: application/json\" \\\\\n -d '{\"test\":\"data\"}' 2>&1)",
118
118
  "Bash(curl -s http://localhost:3000/api/hello 2>&1)",
119
- "Bash(curl -v -X POST \"http://localhost:3000/webhook/075s5s2umn4smn4f\" \\\\\n -H \"Content-Type: application/json\" \\\\\n -d '{\"test\":\"data\"}' 2>&1 | head -30)"
119
+ "Bash(curl -v -X POST \"http://localhost:3000/webhook/075s5s2umn4smn4f\" \\\\\n -H \"Content-Type: application/json\" \\\\\n -d '{\"test\":\"data\"}' 2>&1 | head -30)",
120
+ "Bash(cd D:/code/vb-agent && timeout 30 node test-compression.js 2>&1)"
120
121
  ]
121
122
  }
122
123
  }
package/examples/basic.js CHANGED
@@ -1,110 +1,110 @@
1
- /**
2
- * 基础示例
3
- * 展示如何使用 Framework 和 Agent
4
- */
5
-
6
- const { Framework } = require('../src')
7
- const { AIPlugin } = require('../plugins/ai-plugin')
8
- const { z } = require('zod')
9
-
10
- async function main() {
11
- // 创建框架实例
12
- const framework = new Framework({ debug: true })
13
-
14
- // 加载 AI 插件
15
- await framework.loadPlugin(new AIPlugin({
16
- provider: 'deepseek',
17
- model: 'deepseek-chat',
18
- apiKey: process.env.DEEPSEEK_API_KEY || 'your-api-key'
19
- }))
20
-
21
- // 注册自定义工具(使用 inputSchema 格式)
22
- framework.registerTool({
23
- name: 'hello',
24
- description: '打招呼工具',
25
- inputSchema: z.object({
26
- name: z.string().optional().describe('姓名')
27
- }),
28
- execute: async (args) => {
29
- return `Hello, ${args.name || 'World'}!`
30
- }
31
- })
32
-
33
- // 注册计算器工具
34
- framework.registerTool({
35
- name: 'calculate',
36
- description: '简单的计算器',
37
- inputSchema: z.object({
38
- expression: z.string().describe('数学表达式,如 2+3*4')
39
- }),
40
- execute: async (args) => {
41
- try {
42
- // 安全计算(仅支持基本运算)
43
- const result = Function(`"use strict"; return (${args.expression})`)()
44
- return { result }
45
- } catch (e) {
46
- return { error: e.message }
47
- }
48
- }
49
- })
50
-
51
- console.log('[Framework] Ready!')
52
- console.log('[Tools]', framework.getTools().map(t => t.name))
53
-
54
- // 创建 Agent
55
- const agent = framework.createAgent({
56
- name: 'MyAgent',
57
- systemPrompt: '你是一个有帮助的助手。当需要计算时,使用 calculate 工具。'
58
- })
59
-
60
- // 监听事件
61
- agent.on('tool-call', (tool) => {
62
- console.log('[Agent] Tool call:', tool.name, tool.args)
63
- })
64
-
65
- agent.on('tool-result', (result) => {
66
- console.log('[Agent] Tool result:', result.name, result.result)
67
- })
68
-
69
- // AI 对话示例
70
- console.log('\n=== AI Chat Example ===')
71
- try {
72
- const response = await agent.chat('你好!')
73
- console.log('[Agent] Response:', response.message)
74
- } catch (err) {
75
- console.error('[Agent] Error:', err.message)
76
- }
77
-
78
- // 使用工具的对话示例
79
- console.log('\n=== AI Chat with Tool Call ===')
80
- try {
81
- const response = await agent.chat('请帮我计算 (15 + 25) * 2 等于多少?')
82
- console.log('[Agent] Response:', response.message)
83
- } catch (err) {
84
- console.error('[Agent] Error:', err.message)
85
- }
86
-
87
- // 流式对话示例
88
- console.log('\n=== Streaming Chat ===')
89
- try {
90
- for await (const chunk of agent.chatStream('请用中文介绍一下你自己')) {
91
- if (chunk.type === 'text') {
92
- process.stdout.write(chunk.text)
93
- }
94
- }
95
- console.log('\n')
96
- } catch (err) {
97
- console.error('[Agent] Stream Error:', err.message)
98
- }
99
-
100
- // 热重载示例
101
- console.log('\n=== Hot Reload ===')
102
- await framework.reloadPlugin('ai')
103
- console.log('AI plugin reloaded!')
104
-
105
- // 清理
106
- await framework.destroy()
107
- console.log('\n[Done]')
108
- }
109
-
110
- main().catch(console.error)
1
+ /**
2
+ * 基础示例
3
+ * 展示如何使用 Framework 和 Agent
4
+ */
5
+
6
+ const { Framework } = require('../src')
7
+ const { AIPlugin } = require('../plugins/ai-plugin')
8
+ const { z } = require('zod')
9
+
10
+ async function main() {
11
+ // 创建框架实例
12
+ const framework = new Framework({ debug: true })
13
+
14
+ // 加载 AI 插件
15
+ await framework.loadPlugin(new AIPlugin({
16
+ provider: 'deepseek',
17
+ model: 'deepseek-chat',
18
+ apiKey: process.env.DEEPSEEK_API_KEY || 'your-api-key'
19
+ }))
20
+
21
+ // 注册自定义工具(使用 inputSchema 格式)
22
+ framework.registerTool({
23
+ name: 'hello',
24
+ description: '打招呼工具',
25
+ inputSchema: z.object({
26
+ name: z.string().optional().describe('姓名')
27
+ }),
28
+ execute: async (args) => {
29
+ return `Hello, ${args.name || 'World'}!`
30
+ }
31
+ })
32
+
33
+ // 注册计算器工具
34
+ framework.registerTool({
35
+ name: 'calculate',
36
+ description: '简单的计算器',
37
+ inputSchema: z.object({
38
+ expression: z.string().describe('数学表达式,如 2+3*4')
39
+ }),
40
+ execute: async (args) => {
41
+ try {
42
+ // 安全计算(仅支持基本运算)
43
+ const result = Function(`"use strict"; return (${args.expression})`)()
44
+ return { result }
45
+ } catch (e) {
46
+ return { error: e.message }
47
+ }
48
+ }
49
+ })
50
+
51
+ console.log('[Framework] Ready!')
52
+ console.log('[Tools]', framework.getTools().map(t => t.name))
53
+
54
+ // 创建 Agent
55
+ const agent = framework.createAgent({
56
+ name: 'MyAgent',
57
+ systemPrompt: '你是一个有帮助的助手。当需要计算时,使用 calculate 工具。'
58
+ })
59
+
60
+ // 监听事件
61
+ agent.on('tool-call', (tool) => {
62
+ console.log('[Agent] Tool call:', tool.name, tool.args)
63
+ })
64
+
65
+ agent.on('tool-result', (result) => {
66
+ console.log('[Agent] Tool result:', result.name, result.result)
67
+ })
68
+
69
+ // AI 对话示例
70
+ console.log('\n=== AI Chat Example ===')
71
+ try {
72
+ const response = await agent.chat('你好!')
73
+ console.log('[Agent] Response:', response.message)
74
+ } catch (err) {
75
+ console.error('[Agent] Error:', err.message)
76
+ }
77
+
78
+ // 使用工具的对话示例
79
+ console.log('\n=== AI Chat with Tool Call ===')
80
+ try {
81
+ const response = await agent.chat('请帮我计算 (15 + 25) * 2 等于多少?')
82
+ console.log('[Agent] Response:', response.message)
83
+ } catch (err) {
84
+ console.error('[Agent] Error:', err.message)
85
+ }
86
+
87
+ // 流式对话示例
88
+ console.log('\n=== Streaming Chat ===')
89
+ try {
90
+ for await (const chunk of agent.chatStream('请用中文介绍一下你自己')) {
91
+ if (chunk.type === 'text') {
92
+ process.stdout.write(chunk.text)
93
+ }
94
+ }
95
+ console.log('\n')
96
+ } catch (err) {
97
+ console.error('[Agent] Stream Error:', err.message)
98
+ }
99
+
100
+ // 热重载示例
101
+ console.log('\n=== Hot Reload ===')
102
+ await framework.reloadPlugin('ai')
103
+ console.log('AI plugin reloaded!')
104
+
105
+ // 清理
106
+ await framework.destroy()
107
+ console.log('\n[Done]')
108
+ }
109
+
110
+ main().catch(console.error)
@@ -1,53 +1,53 @@
1
- /**
2
- * MCP 执行器示例
3
- * 展示如何连接 MCP 服务器
4
- */
5
-
6
- const { Framework } = require('../src')
7
- const { MCPExecutorPlugin } = require('../src/executors/mcp-executor')
8
-
9
- async function main() {
10
- console.log('=== MCP Executor Example ===\n')
11
-
12
- // 创建框架
13
- const framework = new Framework({ debug: true })
14
-
15
- // 创建 MCP 执行器插件
16
- const mcpPlugin = new MCPExecutorPlugin({
17
- servers: [
18
- // 示例:添加一个 MCP 服务器
19
- // {
20
- // name: 'example',
21
- // command: 'uvx',
22
- // args: ['example-mcp-server'],
23
- // env: {}
24
- // }
25
- ]
26
- })
27
-
28
- // 加载插件
29
- await framework.loadPlugin(mcpPlugin)
30
-
31
- // 列出服务器
32
- console.log('\n--- MCP Servers ---')
33
- const servers = mcpPlugin.getServers()
34
- console.log('Servers:', servers)
35
-
36
- // 列出所有 MCP 工具
37
- console.log('\n--- MCP Tools ---')
38
- const tools = framework.getTools().filter(t => t.name.startsWith('mcp_'))
39
- console.log('MCP tools:', tools.map(t => t.name))
40
-
41
- // 如果有配置的服务器,可以这样调用:
42
- // const result = await framework.executeTool('mcp_call', {
43
- // server: 'example',
44
- // tool: 'tool_name',
45
- // args: { /* tool arguments */ }
46
- // })
47
-
48
- // 清理
49
- await framework.destroy()
50
- console.log('\n[Done]')
51
- }
52
-
53
- main().catch(console.error)
1
+ /**
2
+ * MCP 执行器示例
3
+ * 展示如何连接 MCP 服务器
4
+ */
5
+
6
+ const { Framework } = require('../src')
7
+ const { MCPExecutorPlugin } = require('../src/executors/mcp-executor')
8
+
9
+ async function main() {
10
+ console.log('=== MCP Executor Example ===\n')
11
+
12
+ // 创建框架
13
+ const framework = new Framework({ debug: true })
14
+
15
+ // 创建 MCP 执行器插件
16
+ const mcpPlugin = new MCPExecutorPlugin({
17
+ servers: [
18
+ // 示例:添加一个 MCP 服务器
19
+ // {
20
+ // name: 'example',
21
+ // command: 'uvx',
22
+ // args: ['example-mcp-server'],
23
+ // env: {}
24
+ // }
25
+ ]
26
+ })
27
+
28
+ // 加载插件
29
+ await framework.loadPlugin(mcpPlugin)
30
+
31
+ // 列出服务器
32
+ console.log('\n--- MCP Servers ---')
33
+ const servers = mcpPlugin.getServers()
34
+ console.log('Servers:', servers)
35
+
36
+ // 列出所有 MCP 工具
37
+ console.log('\n--- MCP Tools ---')
38
+ const tools = framework.getTools().filter(t => t.name.startsWith('mcp_'))
39
+ console.log('MCP tools:', tools.map(t => t.name))
40
+
41
+ // 如果有配置的服务器,可以这样调用:
42
+ // const result = await framework.executeTool('mcp_call', {
43
+ // server: 'example',
44
+ // tool: 'tool_name',
45
+ // args: { /* tool arguments */ }
46
+ // })
47
+
48
+ // 清理
49
+ await framework.destroy()
50
+ console.log('\n[Done]')
51
+ }
52
+
53
+ main().catch(console.error)
@@ -1,49 +1,49 @@
1
- /**
2
- * Skill 管理器示例
3
- * 展示如何加载和使用 Skill
4
- */
5
-
6
- const { Framework } = require('../src')
7
- const { SkillManagerPlugin } = require('../src/capabilities/skill-manager')
8
-
9
- async function main() {
10
- console.log('=== Skill Manager Example ===\n')
11
-
12
- // 创建框架
13
- const framework = new Framework({ debug: true })
14
-
15
- // 创建 skills 目录(如果没有)
16
- const fs = require('fs')
17
- const skillsDir = './skills'
18
- if (!fs.existsSync(skillsDir)) {
19
- fs.mkdirSync(skillsDir, { recursive: true })
20
- }
21
-
22
- // 加载 Skill 管理器插件
23
- const skillPlugin = new SkillManagerPlugin({
24
- skillsDir: skillsDir
25
- })
26
-
27
- await framework.loadPlugin(skillPlugin)
28
-
29
- // 列出所有加载的 skills
30
- console.log('\n--- Loaded Skills ---')
31
- const skills = skillPlugin.getAllSkills()
32
- console.log(`Found ${skills.length} skills:`)
33
- for (const skill of skills) {
34
- console.log(` - ${skill.name}: ${skill.metadata.description}`)
35
- }
36
-
37
- // 获取单个 skill
38
- if (skillPlugin.hasSkill('hello-skill')) {
39
- console.log('\n--- Hello Skill ---')
40
- const helloSkill = skillPlugin.getSkill('hello-skill')
41
- console.log('Content preview:', helloSkill.content.substring(0, 100) + '...')
42
- }
43
-
44
- // 清理
45
- await framework.destroy()
46
- console.log('\n[Done]')
47
- }
48
-
49
- main().catch(console.error)
1
+ /**
2
+ * Skill 管理器示例
3
+ * 展示如何加载和使用 Skill
4
+ */
5
+
6
+ const { Framework } = require('../src')
7
+ const { SkillManagerPlugin } = require('../src/capabilities/skill-manager')
8
+
9
+ async function main() {
10
+ console.log('=== Skill Manager Example ===\n')
11
+
12
+ // 创建框架
13
+ const framework = new Framework({ debug: true })
14
+
15
+ // 创建 skills 目录(如果没有)
16
+ const fs = require('fs')
17
+ const skillsDir = './skills'
18
+ if (!fs.existsSync(skillsDir)) {
19
+ fs.mkdirSync(skillsDir, { recursive: true })
20
+ }
21
+
22
+ // 加载 Skill 管理器插件
23
+ const skillPlugin = new SkillManagerPlugin({
24
+ skillsDir: skillsDir
25
+ })
26
+
27
+ await framework.loadPlugin(skillPlugin)
28
+
29
+ // 列出所有加载的 skills
30
+ console.log('\n--- Loaded Skills ---')
31
+ const skills = skillPlugin.getAllSkills()
32
+ console.log(`Found ${skills.length} skills:`)
33
+ for (const skill of skills) {
34
+ console.log(` - ${skill.name}: ${skill.metadata.description}`)
35
+ }
36
+
37
+ // 获取单个 skill
38
+ if (skillPlugin.hasSkill('hello-skill')) {
39
+ console.log('\n--- Hello Skill ---')
40
+ const helloSkill = skillPlugin.getSkill('hello-skill')
41
+ console.log('Content preview:', helloSkill.content.substring(0, 100) + '...')
42
+ }
43
+
44
+ // 清理
45
+ await framework.destroy()
46
+ console.log('\n[Done]')
47
+ }
48
+
49
+ main().catch(console.error)
@@ -1,79 +1,79 @@
1
- /**
2
- * MCP 插件测试脚本
3
- */
4
-
5
- const { Framework } = require('../src')
6
- const { MCPExecutorPlugin } = require('../src/executors/mcp-executor')
7
-
8
- async function test() {
9
- console.log('=== MCP Plugin Test ===\n')
10
-
11
- console.log('1. Creating framework...')
12
- const framework = new Framework({ debug: true })
13
-
14
- console.log('2. Creating MCP plugin with fetch server...')
15
- const mcpPlugin = new MCPExecutorPlugin({
16
- servers: [
17
- {
18
- name: 'fetch',
19
- command: 'uvx',
20
- args: ['mcp-server-fetch']
21
- }
22
- ]
23
- })
24
-
25
- console.log('3. Loading MCP plugin...')
26
- await framework.loadPlugin(mcpPlugin)
27
-
28
- console.log('4. Starting framework...')
29
- await framework.pluginManager.startAll()
30
-
31
- // 等待 MCP 服务器连接
32
- console.log('\n5. Waiting for MCP connection...')
33
- await new Promise(resolve => setTimeout(resolve, 2000))
34
-
35
- console.log('\n6. Listing MCP servers...')
36
- const servers = mcpPlugin.getServers()
37
- console.log('Servers:', JSON.stringify(servers, null, 2))
38
-
39
- console.log('\n7. Listing MCP tools...')
40
- const mcpTools = framework.getTools().filter(t => t.name.startsWith('mcp_'))
41
- console.log('MCP Tools:', mcpTools.map(t => t.name))
42
-
43
- if (mcpTools.length > 0) {
44
- console.log('\n8. Testing mcp_list_servers...')
45
- const listResult = await framework.executeTool('mcp_list_servers', {})
46
- console.log('List Servers Result:', JSON.stringify(listResult, null, 2))
47
-
48
- console.log('\n9. Testing mcp_tool_schema...')
49
- const schemaResult = await framework.executeTool('mcp_tool_schema', {
50
- server: 'fetch',
51
- tool: 'fetch'
52
- })
53
- console.log('Tool Schema:', JSON.stringify(schemaResult, null, 2))
54
-
55
- console.log('\n10. Testing mcp_call (fetch a webpage)...')
56
- const callResult = await framework.executeTool('mcp_call', {
57
- server: 'fetch',
58
- tool: 'fetch',
59
- args_json: JSON.stringify({ url: 'https://httpbin.org/get' })
60
- })
61
- console.log('Fetch Result (truncated):', JSON.stringify(callResult, null, 2).substring(0, 500) + '...')
62
- }
63
-
64
- console.log('\n11. Destroying framework...')
65
- await framework.destroy()
66
-
67
- console.log('\n=== Test Complete ===')
68
- }
69
-
70
- test()
71
- .then(() => {
72
- console.log('\n✓ Test completed successfully')
73
- process.exit(0)
74
- })
75
- .catch(err => {
76
- console.error('\n✗ Test failed:', err.message)
77
- console.error(err.stack)
78
- process.exit(1)
79
- })
1
+ /**
2
+ * MCP 插件测试脚本
3
+ */
4
+
5
+ const { Framework } = require('../src')
6
+ const { MCPExecutorPlugin } = require('../src/executors/mcp-executor')
7
+
8
+ async function test() {
9
+ console.log('=== MCP Plugin Test ===\n')
10
+
11
+ console.log('1. Creating framework...')
12
+ const framework = new Framework({ debug: true })
13
+
14
+ console.log('2. Creating MCP plugin with fetch server...')
15
+ const mcpPlugin = new MCPExecutorPlugin({
16
+ servers: [
17
+ {
18
+ name: 'fetch',
19
+ command: 'uvx',
20
+ args: ['mcp-server-fetch']
21
+ }
22
+ ]
23
+ })
24
+
25
+ console.log('3. Loading MCP plugin...')
26
+ await framework.loadPlugin(mcpPlugin)
27
+
28
+ console.log('4. Starting framework...')
29
+ await framework.pluginManager.startAll()
30
+
31
+ // 等待 MCP 服务器连接
32
+ console.log('\n5. Waiting for MCP connection...')
33
+ await new Promise(resolve => setTimeout(resolve, 2000))
34
+
35
+ console.log('\n6. Listing MCP servers...')
36
+ const servers = mcpPlugin.getServers()
37
+ console.log('Servers:', JSON.stringify(servers, null, 2))
38
+
39
+ console.log('\n7. Listing MCP tools...')
40
+ const mcpTools = framework.getTools().filter(t => t.name.startsWith('mcp_'))
41
+ console.log('MCP Tools:', mcpTools.map(t => t.name))
42
+
43
+ if (mcpTools.length > 0) {
44
+ console.log('\n8. Testing mcp_list_servers...')
45
+ const listResult = await framework.executeTool('mcp_list_servers', {})
46
+ console.log('List Servers Result:', JSON.stringify(listResult, null, 2))
47
+
48
+ console.log('\n9. Testing mcp_tool_schema...')
49
+ const schemaResult = await framework.executeTool('mcp_tool_schema', {
50
+ server: 'fetch',
51
+ tool: 'fetch'
52
+ })
53
+ console.log('Tool Schema:', JSON.stringify(schemaResult, null, 2))
54
+
55
+ console.log('\n10. Testing mcp_call (fetch a webpage)...')
56
+ const callResult = await framework.executeTool('mcp_call', {
57
+ server: 'fetch',
58
+ tool: 'fetch',
59
+ args_json: JSON.stringify({ url: 'https://httpbin.org/get' })
60
+ })
61
+ console.log('Fetch Result (truncated):', JSON.stringify(callResult, null, 2).substring(0, 500) + '...')
62
+ }
63
+
64
+ console.log('\n11. Destroying framework...')
65
+ await framework.destroy()
66
+
67
+ console.log('\n=== Test Complete ===')
68
+ }
69
+
70
+ test()
71
+ .then(() => {
72
+ console.log('\n✓ Test completed successfully')
73
+ process.exit(0)
74
+ })
75
+ .catch(err => {
76
+ console.error('\n✗ Test failed:', err.message)
77
+ console.error(err.stack)
78
+ process.exit(1)
79
+ })
@@ -1,61 +1,61 @@
1
- /**
2
- * MCP mcp_reload 功能测试
3
- */
4
-
5
- const { Framework } = require('../src')
6
- const { MCPExecutorPlugin } = require('../src/executors/mcp-executor')
7
- const fs = require('fs')
8
- const path = require('path')
9
-
10
- async function test() {
11
- console.log('=== Testing mcp_reload ===\n')
12
-
13
- // 创建框架
14
- const framework = new Framework({ debug: true })
15
-
16
- // 创建 MCP 插件
17
- const mcpPlugin = new MCPExecutorPlugin({
18
- servers: [
19
- { name: 'fetch', command: 'uvx', args: ['mcp-server-fetch'] }
20
- ]
21
- })
22
-
23
- // 加载并启动
24
- await framework.loadPlugin(mcpPlugin)
25
- await framework.pluginManager.startAll()
26
-
27
- // 等待连接
28
- await new Promise(r => setTimeout(r, 2000))
29
-
30
- console.log('\n1. 初始服务器列表:')
31
- const initialServers = mcpPlugin.getServers()
32
- console.log(JSON.stringify(initialServers, null, 2))
33
-
34
- // 测试 mcp_reload
35
- console.log('\n2. 调用 mcp_reload...')
36
- const result = await framework.executeTool('mcp_reload', {})
37
- console.log('mcp_reload 结果:')
38
- console.log(JSON.stringify(result, null, 2))
39
-
40
- // 检查配置
41
- console.log('\n3. 当前配置文件:')
42
- const configPath = path.resolve('.agent/mcp_config.json')
43
- if (fs.existsSync(configPath)) {
44
- const config = JSON.parse(fs.readFileSync(configPath, 'utf8'))
45
- console.log(JSON.stringify(config, null, 2))
46
- } else {
47
- console.log('配置文件不存在: ' + configPath)
48
- }
49
-
50
- // 清理
51
- await framework.destroy()
52
-
53
- console.log('\n=== 测试完成 ===')
54
- }
55
-
56
- test()
57
- .then(() => process.exit(0))
58
- .catch(err => {
59
- console.error('测试失败:', err)
60
- process.exit(1)
61
- })
1
+ /**
2
+ * MCP mcp_reload 功能测试
3
+ */
4
+
5
+ const { Framework } = require('../src')
6
+ const { MCPExecutorPlugin } = require('../src/executors/mcp-executor')
7
+ const fs = require('fs')
8
+ const path = require('path')
9
+
10
+ async function test() {
11
+ console.log('=== Testing mcp_reload ===\n')
12
+
13
+ // 创建框架
14
+ const framework = new Framework({ debug: true })
15
+
16
+ // 创建 MCP 插件
17
+ const mcpPlugin = new MCPExecutorPlugin({
18
+ servers: [
19
+ { name: 'fetch', command: 'uvx', args: ['mcp-server-fetch'] }
20
+ ]
21
+ })
22
+
23
+ // 加载并启动
24
+ await framework.loadPlugin(mcpPlugin)
25
+ await framework.pluginManager.startAll()
26
+
27
+ // 等待连接
28
+ await new Promise(r => setTimeout(r, 2000))
29
+
30
+ console.log('\n1. 初始服务器列表:')
31
+ const initialServers = mcpPlugin.getServers()
32
+ console.log(JSON.stringify(initialServers, null, 2))
33
+
34
+ // 测试 mcp_reload
35
+ console.log('\n2. 调用 mcp_reload...')
36
+ const result = await framework.executeTool('mcp_reload', {})
37
+ console.log('mcp_reload 结果:')
38
+ console.log(JSON.stringify(result, null, 2))
39
+
40
+ // 检查配置
41
+ console.log('\n3. 当前配置文件:')
42
+ const configPath = path.resolve('.agent/mcp_config.json')
43
+ if (fs.existsSync(configPath)) {
44
+ const config = JSON.parse(fs.readFileSync(configPath, 'utf8'))
45
+ console.log(JSON.stringify(config, null, 2))
46
+ } else {
47
+ console.log('配置文件不存在: ' + configPath)
48
+ }
49
+
50
+ // 清理
51
+ await framework.destroy()
52
+
53
+ console.log('\n=== 测试完成 ===')
54
+ }
55
+
56
+ test()
57
+ .then(() => process.exit(0))
58
+ .catch(err => {
59
+ console.error('测试失败:', err)
60
+ process.exit(1)
61
+ })
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "foliko",
3
- "version": "1.0.48",
3
+ "version": "1.0.50",
4
4
  "description": "简约的插件化 Agent 框架",
5
5
  "main": "src/index.js",
6
6
  "bin": {
@@ -40,6 +40,7 @@
40
40
  "node-telegram-bot-api": "^0.67.0",
41
41
  "nodemailer": "^6.10.0",
42
42
  "qrcode-terminal": "^0.12.0",
43
+ "tiktoken": "^1.0.22",
43
44
  "zod": "^3.24.0"
44
45
  }
45
46
  }
package/plugins/email.js CHANGED
@@ -11,7 +11,12 @@ class EmailPlugin extends Plugin {
11
11
  super()
12
12
  this.name = 'email'
13
13
  this.version = '1.0.0'
14
- this.description = '邮件收发插件 - 支持读取和发送电子邮件'
14
+ this.description = `邮件收发插件 - 支持读取和发送电子邮件
15
+ 发送邮件支持附件:
16
+ - attachments.path: 本地文件路径
17
+ - attachments.url: 远程图片/文件URL(自动下载)
18
+ - attachments.content: Base64内容
19
+ - attachments.cid: 嵌入式图片CID(HTML中用 <img src="cid:xxx"> 引用)`
15
20
  this.priority = 10
16
21
  // 默认不启用,需要在 plugins.json 中设置 enabled: true
17
22
  this.enabled = false
@@ -34,7 +39,14 @@ class EmailPlugin extends Plugin {
34
39
  body: z.string().describe('邮件正文内容'),
35
40
  cc: z.string().optional().describe('抄送邮箱地址,多个用逗号分隔'),
36
41
  bcc: z.string().optional().describe('密送邮箱地址,多个用逗号分隔'),
37
- isHtml: z.boolean().optional().describe('是否为HTML格式,默认false')
42
+ isHtml: z.boolean().optional().describe('是否为HTML格式,默认false'),
43
+ attachments: z.array(z.object({
44
+ filename: z.string().describe('附件文件名'),
45
+ path: z.string().optional().describe('本地文件路径'),
46
+ url: z.string().optional().describe('远程图片URL'),
47
+ content: z.string().optional().describe('Base64编码内容(可带前缀如 data:image/png;base64,)'),
48
+ cid: z.string().optional().describe('嵌入式图片的CID(用于在HTML中嵌入图片,如 <img src="cid:xxx">)')
49
+ })).optional().describe('附件列表,支持本地文件、远程URL或Base64内容')
38
50
  }),
39
51
  execute: async (args) => {
40
52
  return this._sendEmail(args)
@@ -119,6 +131,10 @@ class EmailPlugin extends Plugin {
119
131
  async _sendEmail(args) {
120
132
  try {
121
133
  const nodemailer = require('nodemailer')
134
+ const https = require('https')
135
+ const http = require('http')
136
+ const fs = require('fs')
137
+ const path = require('path')
122
138
 
123
139
  const smtpConfig = {
124
140
  host: process.env.SMTP_HOST || 'smtp.gmail.com',
@@ -142,6 +158,32 @@ class EmailPlugin extends Plugin {
142
158
  bcc: args.bcc
143
159
  }
144
160
 
161
+ // 处理附件
162
+ if (args.attachments && args.attachments.length > 0) {
163
+ mailOptions.attachments = []
164
+ for (const att of args.attachments) {
165
+ const attachment = { filename: att.filename }
166
+
167
+ if (att.path) {
168
+ // 本地文件
169
+ attachment.content = fs.createReadStream(att.path)
170
+ } else if (att.url) {
171
+ // 远程URL
172
+ attachment.content = await this._fetchUrl(att.url)
173
+ } else if (att.content) {
174
+ // Base64 内容
175
+ const base64Data = att.content.replace(/^data:[^;]+;base64,/, '')
176
+ attachment.content = Buffer.from(base64Data, 'base64')
177
+ }
178
+
179
+ if (att.cid) {
180
+ attachment.cid = att.cid
181
+ }
182
+
183
+ mailOptions.attachments.push(attachment)
184
+ }
185
+ }
186
+
145
187
  const info = await transporter.sendMail(mailOptions)
146
188
  return {
147
189
  success: true,
@@ -159,6 +201,35 @@ class EmailPlugin extends Plugin {
159
201
  }
160
202
  }
161
203
 
204
+ _fetchUrl(url) {
205
+ return new Promise((resolve, reject) => {
206
+ const protocol = url.startsWith('https') ? https : http
207
+ const lib = url.startsWith('https') ? require('https') : require('http')
208
+
209
+ const request = lib.get(url, (response) => {
210
+ if (response.statusCode === 301 || response.statusCode === 302) {
211
+ // 处理重定向
212
+ this._fetchUrl(response.headers.location).then(resolve).catch(reject)
213
+ return
214
+ }
215
+
216
+ const chunks = []
217
+ response.on('data', (chunk) => chunks.push(chunk))
218
+ response.on('end', () => {
219
+ const buffer = Buffer.concat(chunks)
220
+ resolve(buffer)
221
+ })
222
+ response.on('error', reject)
223
+ })
224
+
225
+ request.on('error', reject)
226
+ request.setTimeout(10000, () => {
227
+ request.destroy()
228
+ reject(new Error('下载超时'))
229
+ })
230
+ })
231
+ }
232
+
162
233
  async _readEmails(args) {
163
234
  try {
164
235
  const Imap = require('imap-mkl')
@@ -32,7 +32,7 @@ class SessionPlugin extends Plugin {
32
32
  this.config = {
33
33
  sessionTTL: config.sessionTTL || 30 * 60 * 1000, // 30分钟
34
34
  maxSessions: config.maxSessions || 100,
35
- maxHistoryLength: config.maxHistoryLength || 50,
35
+ maxHistoryLength: config.maxHistoryLength || 150, // 放宽到 150,Agent 已有智能压缩
36
36
  autoCleanup: config.autoCleanup !== false,
37
37
  cleanupInterval: config.cleanupInterval || 5 * 60 * 1000 // 5分钟
38
38
  }
@@ -256,7 +256,7 @@ class WebPlugin extends Plugin {
256
256
  async _handleRoute(c, route, pathname) {
257
257
  const params = this._extractParams(route.path, pathname)
258
258
  const query = this._parseQuery(c)
259
- const body = await c.req.json().catch(() => ({}))
259
+ const body = await this._parseBody(c)
260
260
 
261
261
  const context = { params, query, body }
262
262
  const result = await this._executeHandler(route.handler, context)
@@ -265,7 +265,7 @@ class WebPlugin extends Plugin {
265
265
 
266
266
  async _handleWebhook(c, webhook) {
267
267
  const query = this._parseQuery(c)
268
- const body = await c.req.json().catch(() => ({}))
268
+ const body = await this._parseBody(c)
269
269
  const webhookData = {
270
270
  path: webhook.path,
271
271
  method: c.req.method,
@@ -490,6 +490,17 @@ class WebPlugin extends Plugin {
490
490
  return query
491
491
  }
492
492
 
493
+ async _parseBody(c) {
494
+ try {
495
+ const rawText = await c.req.text()
496
+ console.log(rawText)
497
+ if (!rawText) return {}
498
+ return JSON.parse(rawText)
499
+ } catch (e) {
500
+ return {}
501
+ }
502
+ }
503
+
493
504
  _getUrl(path='') {
494
505
  if (this._baseUrl) {
495
506
  return `${this._baseUrl}${path}`
@@ -4,6 +4,7 @@
4
4
  */
5
5
 
6
6
  const { EventEmitter } = require('../utils/event-emitter')
7
+ const tiktoken = require('tiktoken')
7
8
 
8
9
  class AgentChatHandler extends EventEmitter {
9
10
  /**
@@ -27,8 +28,172 @@ class AgentChatHandler extends EventEmitter {
27
28
  this._tools = new Map()
28
29
  this._maxSteps = 20
29
30
 
30
- // AI 客户端
31
- this._aiClient = null
31
+ // 上下文压缩配置
32
+ this._maxContextTokens = config.maxContextTokens || 100000
33
+ this._compressionThreshold = config.compressionThreshold || 0.75
34
+ this._keepRecentMessages = config.keepRecentMessages || 30
35
+ this._enableSmartCompress = config.enableSmartCompress !== false // 默认开启智能摘要
36
+ this._encoder = null
37
+ this._compressionCount = 0 // 压缩次数统计
38
+
39
+ // 初始化编码器
40
+ this._initEncoder()
41
+ }
42
+
43
+ /**
44
+ * 初始化 tiktoken 编码器
45
+ * @private
46
+ */
47
+ _initEncoder() {
48
+ try {
49
+ // cl100k_base 是 GPT-4/ChatGPT 使用的编码
50
+ this._encoder = tiktoken.get_encoding('cl100k_base')
51
+ } catch (err) {
52
+ console.warn('[AgentChat] Failed to initialize tiktoken encoder:', err.message)
53
+ }
54
+ }
55
+
56
+ /**
57
+ * 计算文本的 token 数
58
+ * @param {string} text
59
+ * @returns {number}
60
+ * @private
61
+ */
62
+ _countTokens(text) {
63
+ if (!this._encoder || !text) return 0
64
+ try {
65
+ return this._encoder.encode(text).length
66
+ } catch (err) {
67
+ // 粗略估算:约 4 字符 = 1 token
68
+ return Math.ceil(text.length / 4)
69
+ }
70
+ }
71
+
72
+ /**
73
+ * 计算消息列表的总 token 数
74
+ * @param {Array} messages
75
+ * @returns {number}
76
+ * @private
77
+ */
78
+ _countMessagesTokens(messages) {
79
+ let total = 0
80
+ for (const msg of messages) {
81
+ total += 4 // role 标记
82
+ if (typeof msg.content === 'string') {
83
+ total += this._countTokens(msg.content)
84
+ } else if (Array.isArray(msg.content)) {
85
+ for (const part of msg.content) {
86
+ if (part.text) {
87
+ total += this._countTokens(part.text)
88
+ }
89
+ }
90
+ }
91
+ }
92
+ total += 4 // 结尾标记
93
+ return total
94
+ }
95
+
96
+ /**
97
+ * 检查是否需要压缩上下文
98
+ * @returns {boolean}
99
+ * @private
100
+ */
101
+ _shouldCompress() {
102
+ const totalTokens = this._countMessagesTokens(this._messages)
103
+ return totalTokens > this._maxContextTokens * this._compressionThreshold
104
+ }
105
+
106
+ /**
107
+ * 压缩上下文消息(智能摘要模式)
108
+ * 策略:
109
+ * 1. 如果启用了智能摘要且有 AI 客户端,对早期消息进行 AI 总结
110
+ * 2. 否则使用简单裁剪 + 标记
111
+ * @private
112
+ */
113
+ async _compressContext() {
114
+ if (this._messages.length <= this._keepRecentMessages) {
115
+ return
116
+ }
117
+
118
+ const systemMessages = this._messages.filter(m => m.role === 'system')
119
+ const otherMessages = this._messages.filter(m => m.role !== 'system')
120
+
121
+ // 保留最近的 N 条非系统消息
122
+ const recentMessages = otherMessages.slice(-this._keepRecentMessages)
123
+ const messagesToSummarize = otherMessages.slice(0, -this._keepRecentMessages)
124
+
125
+ const compressedCount = messagesToSummarize.length
126
+ let summaryContent = ''
127
+
128
+ // 尝试使用 AI 总结
129
+ if (this._enableSmartCompress && this._aiClient) {
130
+ try {
131
+ const summaryText = await this._summarizeMessages(messagesToSummarize)
132
+ summaryContent = `[早期对话摘要]: ${summaryText}`
133
+ console.log(`[AgentChat] AI 摘要生成成功 (${summaryText.length} chars)`)
134
+ } catch (err) {
135
+ console.warn('[AgentChat] AI 摘要失败,使用简单压缩:', err.message)
136
+ summaryContent = `[上下文已压缩: 省略了 ${compressedCount} 条早期消息。保留了最近 ${this._keepRecentMessages} 条对话记录。]`
137
+ }
138
+ } else {
139
+ summaryContent = `[上下文已压缩: 省略了 ${compressedCount} 条早期消息。保留了最近 ${this._keepRecentMessages} 条对话记录。]`
140
+ }
141
+
142
+ const summary = {
143
+ role: 'system',
144
+ content: summaryContent
145
+ }
146
+
147
+ this._messages = [...systemMessages, summary, ...recentMessages]
148
+ this._compressionCount++
149
+
150
+ const totalTokens = this._countMessagesTokens(this._messages)
151
+ console.log(`[AgentChat] Context compressed (${this._compressionCount} times). Messages: ${this._messages.length}, Est. tokens: ${totalTokens}`)
152
+ }
153
+
154
+ /**
155
+ * 使用 AI 对消息进行总结
156
+ * @param {Array} messages - 要总结的消息
157
+ * @returns {Promise<string>} 总结文本
158
+ * @private
159
+ */
160
+ async _summarizeMessages(messages) {
161
+ if (!this._aiClient || messages.length === 0) {
162
+ return '(无早期对话)'
163
+ }
164
+
165
+ // 构建总结提示
166
+ const conversationText = messages.map(m => {
167
+ const role = m.role === 'user' ? '用户' : '助手'
168
+ const content = typeof m.content === 'string' ? m.content : JSON.stringify(m.content)
169
+ return `${role}: ${content}`
170
+ }).join('\n')
171
+
172
+ const summarizePrompt = `请简洁地总结以下对话的要点,保留关键信息和用户需求:
173
+
174
+ ${conversationText}
175
+
176
+ 总结要求:
177
+ 1. 提取用户的主要需求和意图
178
+ 2. 保留关键的技术细节或决策
179
+ 3. 不要超过 500 字
180
+ 4. 用中文回复`
181
+
182
+ // 使用 AI 生成摘要
183
+ const { ToolLoopAgent } = await this._importAI()
184
+ const agent = new ToolLoopAgent({
185
+ model: this._aiClient,
186
+ instructions: '你是一个对话总结助手。请根据用户提供的对话生成简洁的摘要。',
187
+ tools: []
188
+ })
189
+
190
+ const result = await agent.generate({
191
+ messages: [
192
+ { role: 'user', content: summarizePrompt }
193
+ ]
194
+ })
195
+
196
+ return result.text || '(总结生成失败)'
32
197
  }
33
198
 
34
199
  /**
@@ -63,6 +228,7 @@ class AgentChatHandler extends EventEmitter {
63
228
  */
64
229
  clearHistory() {
65
230
  this._messages = []
231
+ this._compressionCount = 0
66
232
  return this
67
233
  }
68
234
 
@@ -108,6 +274,11 @@ class AgentChatHandler extends EventEmitter {
108
274
 
109
275
  this._messages.push(userMessage)
110
276
 
277
+ // 检查是否需要压缩上下文
278
+ if (this._shouldCompress()) {
279
+ await this._compressContext()
280
+ }
281
+
111
282
  const maxSteps = options.maxSteps || this._maxSteps
112
283
  const tools = this._getAITools(tool)
113
284
 
@@ -169,6 +340,14 @@ class AgentChatHandler extends EventEmitter {
169
340
 
170
341
  this._messages.push(userMessage)
171
342
 
343
+ // 检查是否需要压缩上下文(同步调用,不阻塞流式输出)
344
+ if (this._shouldCompress()) {
345
+ // 压缩在后台进行,不等待完成,避免阻塞流式输出
346
+ this._compressContext().catch(err => {
347
+ console.warn('[AgentChat] Background compression failed:', err.message)
348
+ })
349
+ }
350
+
172
351
  const maxSteps = options.maxSteps || this._maxSteps
173
352
  const tools = this._getAITools(tool)
174
353
 
@@ -301,6 +480,10 @@ class AgentChatHandler extends EventEmitter {
301
480
  destroy() {
302
481
  this._messages = []
303
482
  this._tools.clear()
483
+ if (this._encoder) {
484
+ this._encoder.free()
485
+ this._encoder = null
486
+ }
304
487
  this.removeAllListeners()
305
488
  }
306
489
  }
@@ -1,58 +1,58 @@
1
- /**
2
- * Executor 基类
3
- * 执行器的基类,定义执行器接口
4
- */
5
-
6
- const { EventEmitter } = require('../utils/event-emitter')
7
-
8
- class ExecutorBase extends EventEmitter {
9
- /**
10
- * @param {string} name - 执行器名称
11
- */
12
- constructor(name) {
13
- super()
14
- this.name = name
15
- this._enabled = true
16
- }
17
-
18
- /**
19
- * 执行
20
- * @param {Object} params - 执行参数
21
- * @returns {Promise<any>}
22
- */
23
- async execute(params) {
24
- throw new Error('execute() must be implemented')
25
- }
26
-
27
- /**
28
- * 启用执行器
29
- */
30
- enable() {
31
- this._enabled = true
32
- return this
33
- }
34
-
35
- /**
36
- * 禁用执行器
37
- */
38
- disable() {
39
- this._enabled = false
40
- return this
41
- }
42
-
43
- /**
44
- * 是否启用
45
- */
46
- isEnabled() {
47
- return this._enabled
48
- }
49
-
50
- /**
51
- * 销毁
52
- */
53
- destroy() {
54
- this.removeAllListeners()
55
- }
56
- }
57
-
58
- module.exports = { ExecutorBase }
1
+ /**
2
+ * Executor 基类
3
+ * 执行器的基类,定义执行器接口
4
+ */
5
+
6
+ const { EventEmitter } = require('../utils/event-emitter')
7
+
8
+ class ExecutorBase extends EventEmitter {
9
+ /**
10
+ * @param {string} name - 执行器名称
11
+ */
12
+ constructor(name) {
13
+ super()
14
+ this.name = name
15
+ this._enabled = true
16
+ }
17
+
18
+ /**
19
+ * 执行
20
+ * @param {Object} params - 执行参数
21
+ * @returns {Promise<any>}
22
+ */
23
+ async execute(params) {
24
+ throw new Error('execute() must be implemented')
25
+ }
26
+
27
+ /**
28
+ * 启用执行器
29
+ */
30
+ enable() {
31
+ this._enabled = true
32
+ return this
33
+ }
34
+
35
+ /**
36
+ * 禁用执行器
37
+ */
38
+ disable() {
39
+ this._enabled = false
40
+ return this
41
+ }
42
+
43
+ /**
44
+ * 是否启用
45
+ */
46
+ isEnabled() {
47
+ return this._enabled
48
+ }
49
+
50
+ /**
51
+ * 销毁
52
+ */
53
+ destroy() {
54
+ this.removeAllListeners()
55
+ }
56
+ }
57
+
58
+ module.exports = { ExecutorBase }
package/test-server.js CHANGED
@@ -1,25 +1,25 @@
1
- const { serve } = require('@hono/node-server');
2
- const { Hono } = require('hono');
3
-
4
- const app = new Hono();
5
-
6
- // 简单路由
7
- app.get('/test', (c) => {
8
- console.log('Handler called');
9
- return c.json({ message: 'Hello World' });
10
- });
11
-
12
- const server = serve({
13
- fetch: app.fetch,
14
- port: 3001
15
- });
16
-
17
- server.on('request', (req) => {
18
- console.log('Request:', req.method, req.url);
19
- });
20
-
21
- server.on('error', (err) => {
22
- console.error('Server error:', err);
23
- });
24
-
25
- console.log('Server started on port 3001');
1
+ const { serve } = require('@hono/node-server');
2
+ const { Hono } = require('hono');
3
+
4
+ const app = new Hono();
5
+
6
+ // 简单路由
7
+ app.get('/test', (c) => {
8
+ console.log('Handler called');
9
+ return c.json({ message: 'Hello World' });
10
+ });
11
+
12
+ const server = serve({
13
+ fetch: app.fetch,
14
+ port: 3001
15
+ });
16
+
17
+ server.on('request', (req) => {
18
+ console.log('Request:', req.method, req.url);
19
+ });
20
+
21
+ server.on('error', (err) => {
22
+ console.error('Server error:', err);
23
+ });
24
+
25
+ console.log('Server started on port 3001');
package/test.txt CHANGED
@@ -1,3 +1,3 @@
1
- Hello from static resource test!
2
- This is a test file for verifying static resource serving.
3
- Timestamp: 2026-03-24
1
+ Hello from static resource test!
2
+ This is a test file for verifying static resource serving.
3
+ Timestamp: 2026-03-24