foliko 1.0.29 → 1.0.31

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.
@@ -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)
@@ -9,7 +9,7 @@ const readline = require('readline')
9
9
  require('dotenv').config()
10
10
 
11
11
  async function main() {
12
- console.log('=== VB-Agent 持续聊天测试 ===\n')
12
+ console.log('=== Foliko 持续聊天测试 ===\n')
13
13
  console.log('输入消息与 Agent 对话,输入 exit 或 quit 退出')
14
14
  console.log('多行输入:按两次回车结束,或输入 !! 立即结束\n')
15
15
 
@@ -35,7 +35,7 @@ async function main() {
35
35
  systemPrompt: '你是一个有帮助的助手,擅长回答问题和执行任务。',
36
36
  sharedPrompt: '工作目录: {{WORK_DIR}}',
37
37
  metadata: {
38
- projectName: 'VB-Agent',
38
+ projectName: 'Foliko',
39
39
  version: '1.0.0'
40
40
  }
41
41
  })
@@ -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.29",
3
+ "version": "1.0.31",
4
4
  "description": "简约的插件化 Agent 框架",
5
5
  "main": "src/index.js",
6
6
  "bin": {
package/plugins/email.js CHANGED
@@ -15,16 +15,6 @@ class EmailPlugin extends Plugin {
15
15
  this.priority = 10
16
16
  // 默认不启用,需要在 plugins.json 中设置 enabled: true
17
17
  this.enabled = false
18
- // IMAP ID 信息
19
- this.config = {
20
- ...config,
21
- clientId: config.clientId || {
22
- name: 'FolikoAgent',
23
- version: '1.0.0',
24
- vendor: 'Foliko',
25
- supportEmail: config.imap?.user || 'unknown@example.com'
26
- }
27
- }
28
18
  }
29
19
 
30
20
  install(framework) {
@@ -105,42 +95,22 @@ class EmailPlugin extends Plugin {
105
95
  // 配置邮箱连接
106
96
  this._framework.registerTool({
107
97
  name: 'email_configure',
108
- description: '配置邮箱连接参数',
109
- inputSchema: z.object({
110
- smtp_host: z.string().optional().describe('SMTP服务器地址'),
111
- smtp_port: z.number().optional().describe('SMTP端口'),
112
- smtp_secure: z.boolean().optional().describe('是否使用SSL/TLS'),
113
- smtp_user: z.string().optional().describe('SMTP用户名'),
114
- smtp_pass: z.string().optional().describe('SMTP密码'),
115
- imap_host: z.string().optional().describe('IMAP服务器地址'),
116
- imap_port: z.number().optional().describe('IMAP端口'),
117
- imap_user: z.string().optional().describe('IMAP用户名'),
118
- imap_pass: z.string().optional().describe('IMAP密码'),
119
- from_email: z.string().optional().describe('默认发件人地址'),
120
- client_id_name: z.string().optional().describe('IMAP客户端名称'),
121
- client_id_version: z.string().optional().describe('IMAP客户端版本'),
122
- client_id_vendor: z.string().optional().describe('IMAP客户端厂商'),
123
- client_id_support_email: z.string().optional().describe('IMAP客户端支持邮箱')
124
- }),
125
- execute: async (args) => {
126
- // 处理 clientId 字段
127
- if (args.client_id_name || args.client_id_version || args.client_id_vendor || args.client_id_support_email) {
128
- this.config.clientId = {
129
- name: args.client_id_name || this.config.clientId?.name || 'FolikoAgent',
130
- version: args.client_id_version || this.config.clientId?.version || '1.0.0',
131
- vendor: args.client_id_vendor || this.config.clientId?.vendor || 'Foliko',
132
- supportEmail: args.client_id_support_email || this.config.clientId?.supportEmail
133
- }
134
- delete args.client_id_name
135
- delete args.client_id_version
136
- delete args.client_id_vendor
137
- delete args.client_id_support_email
138
- }
139
- Object.assign(this.config, args)
98
+ description: '查看邮箱配置(实际配置通过环境变量设置)',
99
+ inputSchema: z.object({}),
100
+ execute: async () => {
140
101
  return {
141
102
  success: true,
142
- message: '邮箱配置已更新',
143
- config: this.config
103
+ message: '邮箱配置通过环境变量设置',
104
+ config: {
105
+ smtp_host: process.env.SMTP_HOST || 'smtp.gmail.com',
106
+ smtp_port: process.env.SMTP_PORT || 587,
107
+ smtp_secure: process.env.SMTP_SECURE || 'false',
108
+ imap_host: process.env.IMAP_HOST || 'imap.gmail.com',
109
+ imap_port: process.env.IMAP_PORT || 993,
110
+ from_email: process.env.FROM_EMAIL || '(未设置)',
111
+ client_name: process.env.IMAP_CLIENT_NAME || 'FolikoAgent',
112
+ client_version: process.env.IMAP_CLIENT_VERSION || '1.0.0'
113
+ }
144
114
  }
145
115
  }
146
116
  })
@@ -151,19 +121,19 @@ class EmailPlugin extends Plugin {
151
121
  const nodemailer = require('nodemailer')
152
122
 
153
123
  const smtpConfig = {
154
- host: this.config.smtp_host || process.env.SMTP_HOST || 'smtp.gmail.com',
155
- port: this.config.smtp_port || process.env.SMTP_PORT || 587,
156
- secure: (this.config.smtp_secure || process.env.SMTP_SECURE || 'false') === 'true',
124
+ host: process.env.SMTP_HOST || 'smtp.gmail.com',
125
+ port: parseInt(process.env.SMTP_PORT) || 587,
126
+ secure: process.env.SMTP_SECURE === 'true',
157
127
  auth: {
158
- user: this.config.smtp_user || process.env.SMTP_USER,
159
- pass: this.config.smtp_pass || process.env.SMTP_PASS
128
+ user: process.env.SMTP_USER,
129
+ pass: process.env.SMTP_PASS
160
130
  }
161
131
  }
162
132
 
163
133
  const transporter = nodemailer.createTransport(smtpConfig)
164
134
 
165
135
  const mailOptions = {
166
- from: this.config.from_email || process.env.FROM_EMAIL || smtpConfig.auth.user,
136
+ from: process.env.FROM_EMAIL || smtpConfig.auth.user,
167
137
  to: args.to,
168
138
  subject: args.subject,
169
139
  text: args.isHtml ? undefined : args.body,
@@ -191,20 +161,20 @@ class EmailPlugin extends Plugin {
191
161
 
192
162
  async _readEmails(args) {
193
163
  try {
194
- const Imap = require('imap')
164
+ const Imap = require('imap-mkl')
195
165
  const { simpleParser } = require('mailparser')
196
166
 
197
167
  const imapConfig = {
198
- user: args.user || this.config.imap_user || process.env.IMAP_USER,
199
- password: args.password || this.config.imap_pass || process.env.IMAP_PASS,
200
- host: args.host || this.config.imap_host || process.env.IMAP_HOST,
201
- port: args.port || this.config.imap_port || 993,
168
+ user: args.user || process.env.IMAP_USER,
169
+ password: args.password || process.env.IMAP_PASS,
170
+ host: args.host || process.env.IMAP_HOST,
171
+ port: args.port || parseInt(process.env.IMAP_PORT) || 993,
202
172
  tls: true,
203
173
  tlsOptions: { rejectUnauthorized: false },
204
174
  id: {
205
- name: this.config.clientId?.name || 'FolikoAgent',
206
- version: this.config.clientId?.version || '1.0.0',
207
- vendor: this.config.clientId?.vendor || 'Foliko'
175
+ name: process.env.IMAP_CLIENT_NAME || 'FolikoAgent',
176
+ version: process.env.IMAP_CLIENT_VERSION || '1.0.0',
177
+ vendor: process.env.IMAP_CLIENT_VENDOR || 'Foliko'
208
178
  }
209
179
  }
210
180
 
@@ -230,20 +200,20 @@ class EmailPlugin extends Plugin {
230
200
 
231
201
  async _getUnreadCount(args) {
232
202
  try {
233
- const Imap = require('imap')
203
+ const Imap = require('imap-mkl')
234
204
 
235
205
  const imapConfig = {
236
- user: args.user || this.config.imap_user || process.env.IMAP_USER,
237
- password: args.password || this.config.imap_pass || process.env.IMAP_PASS,
238
- host: args.host || this.config.imap_host || process.env.IMAP_HOST,
239
- port: args.port || this.config.imap_port || 993,
206
+ user: args.user || process.env.IMAP_USER,
207
+ password: args.password || process.env.IMAP_PASS,
208
+ host: args.host || process.env.IMAP_HOST,
209
+ port: args.port || parseInt(process.env.IMAP_PORT) || 993,
240
210
  tls: true,
241
211
  tlsOptions: { rejectUnauthorized: false },
242
212
  id: {
243
- name: this.config.clientId?.name || 'FolikoAgent',
244
- version: this.config.clientId?.version || '1.0.0',
245
- vendor: this.config.clientId?.vendor || 'Foliko',
246
- 'support-email': this.config.clientId?.supportEmail || 'unknown@example.com'
213
+ name: process.env.IMAP_CLIENT_NAME || 'FolikoAgent',
214
+ version: process.env.IMAP_CLIENT_VERSION || '1.0.0',
215
+ vendor: process.env.IMAP_CLIENT_VENDOR || 'Foliko',
216
+ 'support-email': process.env.IMAP_CLIENT_SUPPORT_EMAIL || 'unknown@example.com'
247
217
  }
248
218
  }
249
219
 
@@ -265,20 +235,20 @@ class EmailPlugin extends Plugin {
265
235
 
266
236
  async _markAsRead(args) {
267
237
  try {
268
- const Imap = require('imap')
238
+ const Imap = require('imap-mkl')
269
239
 
270
240
  const imapConfig = {
271
- user: args.user || this.config.imap_user || process.env.IMAP_USER,
272
- password: args.password || this.config.imap_pass || process.env.IMAP_PASS,
273
- host: args.host || this.config.imap_host || process.env.IMAP_HOST,
274
- port: args.port || this.config.imap_port || 993,
241
+ user: args.user || process.env.IMAP_USER,
242
+ password: args.password || process.env.IMAP_PASS,
243
+ host: args.host || process.env.IMAP_HOST,
244
+ port: args.port || parseInt(process.env.IMAP_PORT) || 993,
275
245
  tls: true,
276
246
  tlsOptions: { rejectUnauthorized: false },
277
247
  id: {
278
- name: this.config.clientId?.name || 'FolikoAgent',
279
- version: this.config.clientId?.version || '1.0.0',
280
- vendor: this.config.clientId?.vendor || 'Foliko',
281
- 'support-email': this.config.clientId?.supportEmail || 'unknown@example.com'
248
+ name: process.env.IMAP_CLIENT_NAME || 'FolikoAgent',
249
+ version: process.env.IMAP_CLIENT_VERSION || '1.0.0',
250
+ vendor: process.env.IMAP_CLIENT_VENDOR || 'Foliko',
251
+ 'support-email': process.env.IMAP_CLIENT_SUPPORT_EMAIL || 'unknown@example.com'
282
252
  }
283
253
  }
284
254
 
@@ -314,7 +284,9 @@ class EmailPlugin extends Plugin {
314
284
  return reject(err)
315
285
  }
316
286
 
317
- let searchFilter = searchCriteria ? [searchCriteria] : ['ALL']
287
+ let searchFilter = searchCriteria
288
+ ? searchCriteria.split(' ').filter(Boolean)
289
+ : ['ALL']
318
290
  if (unreadOnly) {
319
291
  searchFilter = ['UNSEEN']
320
292
  }
@@ -334,24 +306,33 @@ class EmailPlugin extends Plugin {
334
306
  const f = imap.fetch(fetchIds, { bodies: '' })
335
307
 
336
308
  f.on('message', (msg) => {
337
- const email = {}
309
+ let email = {}
310
+ let parserDone = false
311
+
312
+ const finishEmail = () => {
313
+ if (parserDone) {
314
+ emails.push(email)
315
+ }
316
+ }
338
317
 
339
318
  msg.on('body', (stream) => {
340
- simpleParser(stream, (err, mail) => {
341
- if (err) {
342
- email.error = err.message
343
- } else {
344
- email.subject = mail.subject
345
- email.from = mail.from?.text || ''
346
- email.to = mail.to?.text || ''
347
- email.date = mail.date?.toISOString() || ''
348
- email.text = mail.text
349
- email.html = mail.html
350
- email.attachments = mail.attachments?.map(a => ({
351
- filename: a.filename,
352
- contentType: a.contentType
353
- })) || []
354
- }
319
+ simpleParser(stream).then(mail => {
320
+ email.subject = mail.subject
321
+ email.from = mail.from?.text || ''
322
+ email.to = mail.to?.text || ''
323
+ email.date = mail.date?.toISOString() || ''
324
+ email.text = mail.text || mail.textAsHtml || ''
325
+ email.html = mail.html
326
+ email.attachments = mail.attachments?.map(a => ({
327
+ filename: a.filename,
328
+ contentType: a.contentType
329
+ })) || []
330
+ parserDone = true
331
+ finishEmail()
332
+ }).catch(err => {
333
+ email.error = err.message
334
+ parserDone = true
335
+ finishEmail()
355
336
  })
356
337
  })
357
338
 
@@ -362,7 +343,8 @@ class EmailPlugin extends Plugin {
362
343
  })
363
344
 
364
345
  msg.on('end', () => {
365
- emails.push(email)
346
+ // 如果没有 body,parserDone 已经是 true
347
+ finishEmail()
366
348
  })
367
349
  })
368
350