foliko 1.0.63 → 1.0.64

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/plugins/email.js CHANGED
@@ -44,19 +44,19 @@ class EmailPlugin extends Plugin {
44
44
  return this
45
45
  }
46
46
 
47
- if (process.env.IMAP_USER && process.env.IMAP_PASS) {
48
- console.log('[Email] Auto-starting email watch...')
49
- this._startEmailWatch({
50
- interval: 60,
51
- host: process.env.IMAP_HOST,
52
- port: parseInt(process.env.IMAP_PORT) || 993,
53
- user: process.env.IMAP_USER,
54
- password: process.env.IMAP_PASS,
55
- box: 'INBOX'
56
- })
57
- } else {
58
- console.log('[Email] IMAP credentials not configured, skipping auto-start')
59
- }
47
+ // if (process.env.IMAP_USER && process.env.IMAP_PASS) {
48
+ // console.log('[Email] Auto-starting email watch...')
49
+ // this._startEmailWatch({
50
+ // interval: 60,
51
+ // host: process.env.IMAP_HOST,
52
+ // port: parseInt(process.env.IMAP_PORT) || 993,
53
+ // user: process.env.IMAP_USER,
54
+ // password: process.env.IMAP_PASS,
55
+ // box: 'INBOX'
56
+ // })
57
+ // } else {
58
+ // console.log('[Email] IMAP credentials not configured, skipping auto-start')
59
+ // }
60
60
  return this
61
61
  }
62
62
 
@@ -148,6 +148,19 @@ class EmailPlugin extends Plugin {
148
148
  }
149
149
  })
150
150
 
151
+ // 删除邮件
152
+ this._framework.registerTool({
153
+ name: 'email_delete',
154
+ description: '删除邮件(标记为已删除,然后永久删除)',
155
+ inputSchema: z.object({
156
+ messageId: z.string().describe('邮件UID或序列号'),
157
+ box: z.string().optional().describe('邮箱文件夹,默认INBOX')
158
+ }),
159
+ execute: async (args) => {
160
+ return this._deleteEmail(args)
161
+ }
162
+ })
163
+
151
164
  // 配置邮箱连接
152
165
  this._framework.registerTool({
153
166
  name: 'email_configure',
@@ -188,126 +201,6 @@ class EmailPlugin extends Plugin {
188
201
  return this._handleEmailWatch(args)
189
202
  }
190
203
  })
191
-
192
- // 自动回复邮件
193
- // this._framework.registerTool({
194
- // name: 'email_auto_reply',
195
- // description: '自动分析邮件内容并发送回复(无需用户确认)',
196
- // inputSchema: z.object({
197
- // to: z.string().describe('收件人邮箱地址'),
198
- // subject: z.string().describe('原始邮件主题'),
199
- // body: z.string().describe('原始邮件内容'),
200
- // from: z.string().optional().describe('发件人邮箱地址(可选)'),
201
- // prompt: z.string().optional().describe('自定义提示词,用于指导AI生成回复内容')
202
- // }),
203
- // execute: async (args) => {
204
- // return this._handleAutoReply(args)
205
- // }
206
- // })
207
- }
208
-
209
- /**
210
- * 处理自动回复
211
- */
212
- async _handleAutoReply(args) {
213
- let { to, subject, body, from, _event, prompt } = args
214
-
215
- // 如果没有直接参数,尝试从 _event 提取
216
- if (!to && !subject && !body && _event) {
217
- const email = _event.data?.email || _event.email || {}
218
- from = email.from?.text || email.from || ''
219
- to = email.to?.text || email.to || ''
220
- subject = email.subject || ''
221
- body = email.text || email.body || ''
222
- }
223
-
224
- // 检查必要参数
225
- if (!from && !to) {
226
- return { success: false, error: '缺少收件人地址' }
227
- }
228
- if (!body) {
229
- return { success: false, error: '缺少邮件内容' }
230
- }
231
-
232
- try {
233
- // 获取活跃的 Agent
234
- const agent = this._getActiveAgent()
235
- if (!agent) {
236
- return { success: false, error: 'No active agent found' }
237
- }
238
-
239
- // 构建提示让 LLM 生成回复
240
- const finalPrompt = prompt || `你是一封邮件自动回复助手。请根据以下邮件内容,生成一封专业的回复邮件。
241
-
242
- 【原始邮件】
243
- 发件人: ${from || to}
244
- 主题: ${subject}
245
- 内容:
246
- ${body}
247
-
248
- 【要求】
249
- 1. 回复内容要针对邮件中的问题或内容进行回复
250
- 2. 语言要专业、礼貌、简洁
251
- 3. 只输出邮件正文内容,不要额外解释
252
- 4. 回复语言应与原邮件一致(如果原邮件是中文,则用中文回复)`
253
-
254
- // 等待 Agent 生成回复(带超时保护)
255
- const timeoutPromise = new Promise((_, reject) => {
256
- setTimeout(() => reject(new Error('AI回复生成超时(30秒)')), 30000)
257
- })
258
-
259
- const replyPromise = agent.pushMessage(finalPrompt, { maxSteps: 3 })
260
- const replyResult = await Promise.race([replyPromise, timeoutPromise])
261
-
262
- // 提取回复内容
263
- let replyContent = ''
264
- if (typeof replyResult === 'string') {
265
- replyContent = replyResult.trim()
266
- } else if (replyResult && replyResult.content) {
267
- replyContent = replyResult.content.trim()
268
- } else if (replyResult && replyResult.message) {
269
- replyContent = replyResult.message.trim()
270
- } else {
271
- replyContent = JSON.stringify(replyResult).trim()
272
- }
273
-
274
- // 去掉思考过程标签
275
- replyContent = replyContent.replace(/<think>[\s\S]*?<\/think>/g, '').trim()
276
-
277
- if (!replyContent || replyContent.length < 5) {
278
- return { success: false, error: 'AI未能生成有效的回复内容' }
279
- }
280
-
281
- // 发送回复邮件
282
- const sendResult = await this._sendEmail({
283
- to: from || to,
284
- subject: `Re: ${subject}`,
285
- body: replyContent
286
- })
287
-
288
- if (sendResult.success) {
289
- return {
290
- success: true,
291
- message: `自动回复已发送至 ${from || to}`,
292
- replyContent
293
- }
294
- } else {
295
- return { success: false, error: sendResult.error || '发送失败' }
296
- }
297
- } catch (err) {
298
- return { success: false, error: err.message }
299
- }
300
- }
301
-
302
- /**
303
- * 获取活跃的 Agent
304
- */
305
- _getActiveAgent() {
306
- if (this._framework._mainAgent) {
307
- return this._framework._mainAgent
308
- }
309
- const agents = this._framework._agents || []
310
- return agents.length > 0 ? agents[agents.length - 1] : null
311
204
  }
312
205
 
313
206
  /**
@@ -316,10 +209,10 @@ ${body}
316
209
  async _handleEmailWatch(args) {
317
210
  args={
318
211
  ...args,
319
- host: process.env.IMAP_HOST,
320
- port: parseInt(process.env.IMAP_PORT) || 993,
321
- user: process.env.IMAP_USER,
322
- password: process.env.IMAP_PASS
212
+ host: config.host || process.env.IMAP_HOST,
213
+ port: config.port || parseInt(process.env.IMAP_PORT) || 993,
214
+ user: config.user || process.env.IMAP_USER,
215
+ password: config.password || process.env.IMAP_PASS
323
216
  }
324
217
 
325
218
  const { action, interval, host, port, user, password, box } = args
@@ -514,7 +407,7 @@ ${body}
514
407
  // 发送事件通知
515
408
  this._emitEmailReceived(email)
516
409
 
517
- console.log(`[Email] New email received: ${email.subject}`)
410
+ //console.log(`[Email] New email received: ${email.subject}`)
518
411
  cleanup()
519
412
  resolve({ success: true, newEmails: newEmails.length, email })
520
413
  } else {
@@ -757,6 +650,40 @@ ${body}
757
650
  }
758
651
  }
759
652
 
653
+ async _deleteEmail(args) {
654
+ try {
655
+ const Imap = require('imap-mkl')
656
+
657
+ const imapConfig = {
658
+ user: args.user || process.env.IMAP_USER,
659
+ password: args.password || process.env.IMAP_PASS,
660
+ host: args.host || process.env.IMAP_HOST,
661
+ port: args.port || parseInt(process.env.IMAP_PORT) || 993,
662
+ tls: true,
663
+ tlsOptions: { rejectUnauthorized: false },
664
+ id: {
665
+ name: process.env.IMAP_CLIENT_NAME || 'FolikoAgent',
666
+ version: process.env.IMAP_CLIENT_VERSION || '1.0.0',
667
+ vendor: process.env.IMAP_CLIENT_VENDOR || 'Foliko',
668
+ 'support-email': process.env.IMAP_CLIENT_SUPPORT_EMAIL || 'unknown@example.com'
669
+ }
670
+ }
671
+
672
+ const box = args.box || 'INBOX'
673
+ await this._moveToTrash(imapConfig, box, args.messageId)
674
+
675
+ return {
676
+ success: true,
677
+ message: '邮件已删除'
678
+ }
679
+ } catch (error) {
680
+ return {
681
+ success: false,
682
+ error: error.message
683
+ }
684
+ }
685
+ }
686
+
760
687
  _fetchEmails(imapConfig, box, limit, unreadOnly, searchCriteria) {
761
688
  return new Promise((resolve, reject) => {
762
689
  const Imap = require('imap-mkl')
@@ -924,6 +851,44 @@ ${body}
924
851
  })
925
852
  }
926
853
 
854
+ _moveToTrash(imapConfig, box, messageId) {
855
+ return new Promise((resolve, reject) => {
856
+ const Imap = require('imap-mkl')
857
+ const imap = new Imap(imapConfig)
858
+
859
+ const cleanup = () => {
860
+ try { imap.end() } catch (e) {}
861
+ }
862
+
863
+ imap.on('ready', () => {
864
+ imap.openBox(box, true, (err) => {
865
+ if (err) {
866
+ cleanup()
867
+ return reject(err)
868
+ }
869
+ // 标记邮件为已删除
870
+ imap.addFlags(messageId, '\\Deleted', (err) => {
871
+ if (err) {
872
+ cleanup()
873
+ return reject(err)
874
+ }
875
+ // 执行 expunge 永久删除
876
+ imap.expunge((err) => {
877
+ cleanup()
878
+ if (err) reject(err)
879
+ else resolve()
880
+ })
881
+ })
882
+ })
883
+ })
884
+
885
+ imap.on('error', (err) => reject(err))
886
+ imap.on('end', () => {})
887
+
888
+ imap.connect()
889
+ })
890
+ }
891
+
927
892
  uninstall(framework) {
928
893
  // 停止邮件监控
929
894
  if (this._watchInterval) {
@@ -394,6 +394,36 @@ class FileSystemPlugin extends Plugin {
394
394
  }
395
395
  })
396
396
 
397
+ // 发送通知
398
+ framework.registerTool({
399
+ name: 'notification_send',
400
+ description: '发送系统通知,仅发送给当前聊天会话,通知会显示给用户或在下次对话时呈现',
401
+ inputSchema: z.object({
402
+ title: z.string().describe('通知标题'),
403
+ message: z.string().describe('通知内容'),
404
+ source: z.string().optional().describe('通知来源标识,默认 file-system')
405
+ }),
406
+ execute: async (args, framework) => {
407
+ const { title, message, source = 'file-system' } = args
408
+ try {
409
+ // 获取当前执行上下文中的 sessionId,只发送到当前会话
410
+ const ctx = framework.getExecutionContext()
411
+ const sessionId = ctx?.sessionId || null
412
+
413
+ framework.emit('notification', {
414
+ title,
415
+ message,
416
+ source,
417
+ sessionId,
418
+ timestamp: new Date().toISOString()
419
+ })
420
+ return { success: true, message: '通知已发送' }
421
+ } catch (error) {
422
+ return { success: false, error: error.message }
423
+ }
424
+ }
425
+ })
426
+
397
427
  return this
398
428
  }
399
429
  }
@@ -203,7 +203,7 @@ class SchedulerPlugin extends Plugin {
203
203
  return bTime - aTime
204
204
  })
205
205
  targetSessionId = sessions[0].id
206
- console.log(`[Scheduler] Auto-detected active session: ${targetSessionId}`)
206
+ //console.log(`[Scheduler] Auto-detected active session: ${targetSessionId}`)
207
207
  }
208
208
  }
209
209
  }
@@ -534,12 +534,12 @@ class SchedulerPlugin extends Plugin {
534
534
  * 执行任务
535
535
  */
536
536
  async _executeTask(task) {
537
- console.log(`[Scheduler] Executing task: ${task.name} (${task.id})`)
538
- console.log(`[Scheduler] Message: ${task.message}`)
539
- if (task.sessionId) {
540
- console.log(`[Scheduler] Target session: ${task.sessionId}`)
541
- }
542
- console.log(`[Scheduler] LLM mode: ${task.llm ? 'enabled' : 'disabled'}`)
537
+ // console.log(`[Scheduler] Executing task: ${task.name} (${task.id})`)
538
+ // console.log(`[Scheduler] Message: ${task.message}`)
539
+ // if (task.sessionId) {
540
+ // console.log(`[Scheduler] Target session: ${task.sessionId}`)
541
+ // }
542
+ // console.log(`[Scheduler] LLM mode: ${task.llm ? 'enabled' : 'disabled'}`)
543
543
 
544
544
  task.lastRun = new Date()
545
545
  task.runCount++
@@ -569,9 +569,9 @@ class SchedulerPlugin extends Plugin {
569
569
  responseText = result.text
570
570
  }
571
571
 
572
- if (responseText) {
573
- console.log(`\n🔔 [定时提醒] ${responseText}\n`)
574
- }
572
+ // if (responseText) {
573
+ // console.log(`\n🔔 [定时提醒] ${responseText}\n`)
574
+ // }
575
575
 
576
576
  // 发送统一的通知事件
577
577
  this._framework.emit('notification', {
@@ -584,7 +584,7 @@ class SchedulerPlugin extends Plugin {
584
584
  })
585
585
  } else {
586
586
  // 直接显示模式:只显示提醒,不发 LLM
587
- console.log(`\n🔔 [定时提醒] ${task.message}\n`)
587
+ //console.log(`\n🔔 [定时提醒] ${task.message}\n`)
588
588
 
589
589
  // 发送统一的通知事件
590
590
  this._framework.emit('notification', {
package/src/core/agent.js CHANGED
@@ -29,7 +29,7 @@ class Agent extends EventEmitter {
29
29
  this.baseURL = config.baseURL
30
30
  this.provider = config.provider || 'deepseek'
31
31
  this.providerOptions = config.providerOptions || {}
32
- this.providerOptions.maxOutputTokens=8192
32
+
33
33
  // 原始 system prompt
34
34
  this._originalPrompt = config.systemPrompt || '你是一个智能助手。当用户提出问题或任务时,你会主动分析需求,选择合适的工具来获取信息或执行操作。你善于将复杂任务拆解为多个步骤,通过工具协作完成。'
35
35
 
@@ -215,7 +215,7 @@ class Framework extends EventEmitter {
215
215
  name: `session_${sessionId}`,
216
216
  ...config
217
217
  }
218
-
218
+
219
219
  // 如果没有提供 AI 相关参数,从 AI 插件获取
220
220
  if (!agentConfig.apiKey) {
221
221
  const aiPlugin = this.pluginManager.get('ai')
@@ -223,10 +223,10 @@ class Framework extends EventEmitter {
223
223
  agentConfig.apiKey = aiPlugin.config.apiKey
224
224
  agentConfig.provider = agentConfig.provider || aiPlugin.config.provider
225
225
  agentConfig.model = agentConfig.model || aiPlugin.config.model
226
- agentConfig.baseURL = agentConfig.baseURL || aiPlugin.config.baseURL,
227
- agentConfig.providerOptions||aiPlugin.config.providerOptions||{}
226
+ agentConfig.baseURL = agentConfig.baseURL || aiPlugin.config.baseURL
228
227
  }
229
228
  }
229
+
230
230
  const agent = new Agent(this, agentConfig)
231
231
  this._agents.push(agent)
232
232
 
@@ -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