foliko 1.0.53 → 1.0.55

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
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Email 插件
3
- * 邮件收发插件 - 支持读取和发送电子邮件
3
+ * 邮件收发插件 - 支持读取和发送电子邮件、监控新邮件
4
4
  */
5
5
 
6
6
  const { Plugin } = require('../src/core/plugin-base')
@@ -10,16 +10,27 @@ class EmailPlugin extends Plugin {
10
10
  constructor(config = {}) {
11
11
  super()
12
12
  this.name = 'email'
13
- this.version = '1.0.0'
14
- this.description = `邮件收发插件 - 支持读取和发送电子邮件
13
+ this.version = '1.1.0'
14
+ this.description = `邮件收发插件 - 支持读取和发送电子邮件、监控新邮件、自动回复,配置已经预设,不用获取配置
15
+ 功能:
16
+ - 发送邮件支持附件(本地文件、远程URL、Base64)
17
+ - 读取邮件(IMAP协议)
18
+ - 监控新邮件(支持IMAP IDLE推送和定时轮询)
19
+ - 自动回复邮件(AI分析内容并发送回复,无需用户确认)
15
20
  发送邮件支持附件:
16
21
  - attachments.path: 本地文件路径
17
22
  - attachments.url: 远程图片/文件URL(自动下载)
18
23
  - attachments.content: Base64内容
19
24
  - attachments.cid: 嵌入式图片CID(HTML中用 <img src="cid:xxx"> 引用)`
20
25
  this.priority = 10
21
- // 默认不启用,需要在 plugins.json 中设置 enabled: true
22
26
  this.enabled = false
27
+
28
+ // 邮件监控状态
29
+ this._watchInterval = null
30
+ this._lastSeenUid = null
31
+ this._watchConfig = null
32
+ this._watchEnabled = false
33
+ this._checking = false // 防止并发检查
23
34
  }
24
35
 
25
36
  install(framework) {
@@ -28,6 +39,41 @@ class EmailPlugin extends Plugin {
28
39
  return this
29
40
  }
30
41
 
42
+ start(framework) {
43
+ // 自动启动邮件监控(如果配置了 IMAP 且尚未运行)
44
+ if (this._watchEnabled) {
45
+ console.log('[Email] Email watch already running, skipping auto-start')
46
+ return this
47
+ }
48
+
49
+ // if (process.env.IMAP_USER && process.env.IMAP_PASS) {
50
+ // console.log('[Email] Auto-starting email watch...')
51
+ // this._startEmailWatch({
52
+ // interval: 60,
53
+ // host: process.env.IMAP_HOST,
54
+ // port: parseInt(process.env.IMAP_PORT) || 993,
55
+ // user: process.env.IMAP_USER,
56
+ // password: process.env.IMAP_PASS,
57
+ // box: 'INBOX'
58
+ // })
59
+ // } else {
60
+ // console.log('[Email] IMAP credentials not configured, skipping auto-start')
61
+ // }
62
+ return this
63
+ }
64
+
65
+ /**
66
+ * 发送邮件事件
67
+ */
68
+ _emitEmailReceived(email) {
69
+ if (this._framework) {
70
+ this._framework.emit('email:received', {
71
+ email,
72
+ timestamp: new Date()
73
+ })
74
+ }
75
+ }
76
+
31
77
  _registerTools() {
32
78
  // 发送邮件工具
33
79
  this._framework.registerTool({
@@ -105,26 +151,413 @@ class EmailPlugin extends Plugin {
105
151
  })
106
152
 
107
153
  // 配置邮箱连接
154
+ // this._framework.registerTool({
155
+ // name: 'email_configure',
156
+ // description: '查看邮箱配置(实际配置通过环境变量设置)',
157
+ // inputSchema: z.object({}),
158
+ // execute: async () => {
159
+ // return {
160
+ // success: true,
161
+ // message: '邮箱配置通过环境变量设置',
162
+ // config: {
163
+ // smtp_host: process.env.SMTP_HOST || 'smtp.gmail.com',
164
+ // smtp_port: process.env.SMTP_PORT || 587,
165
+ // smtp_secure: process.env.SMTP_SECURE || 'false',
166
+ // imap_host: process.env.IMAP_HOST || 'imap.gmail.com',
167
+ // imap_port: process.env.IMAP_PORT || 993,
168
+ // from_email: process.env.FROM_EMAIL || '(未设置)',
169
+ // client_name: process.env.IMAP_CLIENT_NAME || 'FolikoAgent',
170
+ // client_version: process.env.IMAP_CLIENT_VERSION || '1.0.0'
171
+ // }
172
+ // }
173
+ // }
174
+ // })
175
+
176
+ // 监控新邮件
108
177
  this._framework.registerTool({
109
- name: 'email_configure',
110
- description: '查看邮箱配置(实际配置通过环境变量设置)',
111
- inputSchema: z.object({}),
112
- execute: async () => {
178
+ name: 'email_watch',
179
+ description: '启动/停止邮件监控,检测新邮件并发送事件通知',
180
+ inputSchema: z.object({
181
+ action: z.enum(['start', 'stop', 'status']).describe('操作:启动监控、停止监控、查看状态'),
182
+ interval: z.number().optional().describe('轮询间隔(秒),默认60秒,适用于不支持IDLE的服务器'),
183
+ // host: z.string().optional().describe('IMAP服务器地址'),
184
+ // port: z.number().optional().describe('IMAP端口'),
185
+ // user: z.string().optional().describe('邮箱用户名'),
186
+ // password: z.string().optional().describe('邮箱密码'),
187
+ box: z.string().optional().describe('邮箱文件夹,默认INBOX')
188
+ }),
189
+ execute: async (args) => {
190
+
191
+ return this._handleEmailWatch({
192
+ ...args,
193
+ host: process.env.IMAP_HOST,
194
+ port: parseInt(process.env.IMAP_PORT) || 993,
195
+ user: process.env.IMAP_USER,
196
+ password: process.env.IMAP_PASS,
197
+ })
198
+ }
199
+ })
200
+
201
+ // 自动回复邮件
202
+ this._framework.registerTool({
203
+ name: 'email_auto_reply',
204
+ description: '自动分析邮件内容并发送回复(无需用户确认)',
205
+ inputSchema: z.object({
206
+ to: z.string().describe('收件人邮箱地址'),
207
+ subject: z.string().describe('原始邮件主题'),
208
+ body: z.string().describe('原始邮件内容'),
209
+ from: z.string().optional().describe('发件人邮箱地址(可选)')
210
+ }),
211
+ execute: async (args) => {
212
+ return this._handleAutoReply(args)
213
+ }
214
+ })
215
+ }
216
+
217
+ /**
218
+ * 处理自动回复
219
+ */
220
+ async _handleAutoReply(args) {
221
+ // 支持两种模式:
222
+ // 1. 直接传参数: { to, subject, body, from }
223
+ // 2. 从 _event 提取: { _event } (来自 ambient agent 的事件触发)
224
+ let { to, subject, body, from, _event } = args
225
+
226
+ // 如果没有直接参数,尝试从 _event 提取
227
+ if (!to && !subject && !body && _event) {
228
+ const email = _event.data?.email || _event.email || {}
229
+ from = email.from?.text || email.from || ''
230
+ to = email.to?.text || email.to || ''
231
+ subject = email.subject || ''
232
+ body = email.text || email.body || ''
233
+ }
234
+
235
+ // 检查必要参数
236
+ if (!from && !to) {
237
+ return { success: false, error: '缺少收件人地址' }
238
+ }
239
+ if (!body) {
240
+ return { success: false, error: '缺少邮件内容' }
241
+ }
242
+
243
+ try {
244
+ // 获取活跃的 Agent
245
+ const agent = this._getActiveAgent()
246
+ if (!agent) {
247
+ return { success: false, error: 'No active agent found' }
248
+ }
249
+
250
+ // 构建提示让 LLM 生成回复
251
+ const prompt = `你是一封邮件自动回复助手。请根据以下邮件内容,生成一封专业的回复邮件。
252
+
253
+ 【原始邮件】
254
+ 发件人: ${from || to}
255
+ 主题: ${subject}
256
+ 内容:
257
+ ${body}
258
+
259
+ 【要求】
260
+ 1. 回复内容要针对邮件中的问题或内容进行回复
261
+ 2. 语言要专业、礼貌、简洁
262
+ 3. 只输出邮件正文内容,不要额外解释
263
+ 4. 回复语言应与原邮件一致(如果原邮件是中文,则用中文回复)`
264
+
265
+ // 等待 Agent 生成回复(带超时保护)
266
+ const timeoutPromise = new Promise((_, reject) => {
267
+ setTimeout(() => reject(new Error('AI回复生成超时(30秒)')), 30000)
268
+ })
269
+
270
+ const replyPromise = agent.pushMessage(prompt, { maxSteps: 3 })
271
+ const replyResult = await Promise.race([replyPromise, timeoutPromise])
272
+
273
+ // 提取回复内容
274
+ let replyContent = ''
275
+ if (typeof replyResult === 'string') {
276
+ replyContent = replyResult.trim()
277
+ } else if (replyResult && replyResult.content) {
278
+ replyContent = replyResult.content.trim()
279
+ } else if (replyResult && replyResult.message) {
280
+ replyContent = replyResult.message.trim()
281
+ } else {
282
+ replyContent = JSON.stringify(replyResult).trim()
283
+ }
284
+
285
+ // 去掉 <</think> 思考过程标签
286
+ replyContent = replyContent.replace(/<think>[\s\S]*?<\/think>/g, '').trim()
287
+
288
+ // 如果去完后内容为空或太短,说明提取失败
289
+ if (replyContent.length < 5) {
290
+ return { success: false, error: 'AI回复内容太短或无效' }
291
+ }
292
+
293
+ if (!replyContent) {
294
+ return { success: false, error: 'AI未能生成有效的回复内容' }
295
+ }
296
+
297
+ // 发送回复邮件
298
+ const sendResult = await this._sendEmail({
299
+ to: from || to,
300
+ subject: `Re: ${subject}`,
301
+ body: replyContent
302
+ })
303
+
304
+ if (sendResult.success) {
305
+ console.log(`[Email] Auto reply sent to ${from || to}`)
113
306
  return {
114
307
  success: true,
115
- message: '邮箱配置通过环境变量设置',
116
- config: {
117
- smtp_host: process.env.SMTP_HOST || 'smtp.gmail.com',
118
- smtp_port: process.env.SMTP_PORT || 587,
119
- smtp_secure: process.env.SMTP_SECURE || 'false',
120
- imap_host: process.env.IMAP_HOST || 'imap.gmail.com',
121
- imap_port: process.env.IMAP_PORT || 993,
122
- from_email: process.env.FROM_EMAIL || '(未设置)',
123
- client_name: process.env.IMAP_CLIENT_NAME || 'FolikoAgent',
124
- client_version: process.env.IMAP_CLIENT_VERSION || '1.0.0'
125
- }
308
+ message: `自动回复已发送至 ${from || to}`,
309
+ replyContent
126
310
  }
311
+ } else {
312
+ return { success: false, error: sendResult.error || '发送失败' }
127
313
  }
314
+ } catch (err) {
315
+ console.error('[Email] Auto reply error:', err.message)
316
+ return { success: false, error: err.message }
317
+ }
318
+ }
319
+
320
+ /**
321
+ * 获取活跃的 Agent
322
+ */
323
+ _getActiveAgent() {
324
+ if (this._framework._mainAgent) {
325
+ return this._framework._mainAgent
326
+ }
327
+ const agents = this._framework._agents || []
328
+ return agents.length > 0 ? agents[agents.length - 1] : null
329
+ }
330
+
331
+ /**
332
+ * 处理邮件监控
333
+ */
334
+ async _handleEmailWatch(args) {
335
+ const { action, interval, host, port, user, password, box } = args
336
+
337
+ switch (action) {
338
+ case 'start':
339
+ return this._startEmailWatch({ interval, host, port, user, password, box })
340
+
341
+ case 'stop':
342
+ return this._stopEmailWatch()
343
+
344
+ case 'status':
345
+ return this._getEmailWatchStatus()
346
+
347
+ default:
348
+ return { success: false, error: `Unknown action: ${action}` }
349
+ }
350
+ }
351
+
352
+ /**
353
+ * 启动邮件监控
354
+ */
355
+ _startEmailWatch(config) {
356
+ if (this._watchEnabled) {
357
+ return { success: false, error: 'Email watch is already running' }
358
+ }
359
+
360
+ this._watchConfig = {
361
+ host: config.host || process.env.IMAP_HOST,
362
+ port: config.port || parseInt(process.env.IMAP_PORT) || 993,
363
+ user: config.user || process.env.IMAP_USER,
364
+ password: config.password || process.env.IMAP_PASS,
365
+ box: config.box || 'INBOX',
366
+ interval: (config.interval || 60) * 1000 // 转换为毫秒
367
+ }
368
+
369
+ if (!this._watchConfig.user || !this._watchConfig.password) {
370
+ return { success: false, error: 'IMAP user/password is required' }
371
+ }
372
+
373
+ this._watchEnabled = true
374
+ console.log('[Email] Starting email watch...')
375
+
376
+ // 立即执行一次检查
377
+ this._checkNewEmails().catch(err => {
378
+ console.error('[Email] Initial check failed:', err.message)
379
+ })
380
+
381
+ // 启动轮询
382
+ this._watchInterval = setInterval(() => {
383
+ this._checkNewEmails().catch(err => {
384
+ console.error('[Email] Watch check failed:', err.message)
385
+ })
386
+ }, this._watchConfig.interval)
387
+
388
+ return {
389
+ success: true,
390
+ message: `Email watch started (interval: ${this._watchConfig.interval / 1000}s)`,
391
+ config: {
392
+ host: this._watchConfig.host,
393
+ box: this._watchConfig.box,
394
+ interval: this._watchConfig.interval / 1000
395
+ }
396
+ }
397
+ }
398
+
399
+ /**
400
+ * 停止邮件监控
401
+ */
402
+ _stopEmailWatch() {
403
+ if (!this._watchEnabled) {
404
+ return { success: false, error: 'Email watch is not running' }
405
+ }
406
+
407
+ if (this._watchInterval) {
408
+ clearInterval(this._watchInterval)
409
+ this._watchInterval = null
410
+ }
411
+
412
+ this._watchEnabled = false
413
+ this._watchConfig = null
414
+
415
+ console.log('[Email] Email watch stopped')
416
+ return { success: true, message: 'Email watch stopped' }
417
+ }
418
+
419
+ /**
420
+ * 获取监控状态
421
+ */
422
+ _getEmailWatchStatus() {
423
+ return {
424
+ success: true,
425
+ watching: this._watchEnabled,
426
+ config: this._watchConfig ? {
427
+ host: this._watchConfig.host,
428
+ box: this._watchConfig.box,
429
+ interval: this._watchConfig.interval / 1000,
430
+ lastSeenUid: this._lastSeenUid
431
+ } : null
432
+ }
433
+ }
434
+
435
+ /**
436
+ * 检查新邮件
437
+ */
438
+ async _checkNewEmails() {
439
+ if (!this._watchConfig) return
440
+ if (this._checking) return // 防止并发检查
441
+ this._checking = true
442
+
443
+ const Imap = require('imap-mkl')
444
+ const { simpleParser } = require('mailparser')
445
+
446
+ const imapConfig = {
447
+ user: this._watchConfig.user,
448
+ password: this._watchConfig.password,
449
+ host: this._watchConfig.host,
450
+ port: this._watchConfig.port,
451
+ tls: true,
452
+ tlsOptions: { rejectUnauthorized: false },
453
+ id: {
454
+ name: process.env.IMAP_CLIENT_NAME || 'FolikoAgent',
455
+ version: process.env.IMAP_CLIENT_VERSION || '1.0.0',
456
+ vendor: process.env.IMAP_CLIENT_VENDOR || 'Foliko'
457
+ }
458
+ }
459
+
460
+ return new Promise((resolve, reject) => {
461
+ const imap = new Imap(imapConfig)
462
+
463
+ const cleanup = () => {
464
+ try { imap.end() } catch (e) {}
465
+ this._checking = false
466
+ }
467
+
468
+ imap.on('ready', () => {
469
+ imap.openBox(this._watchConfig.box, true, (err, box) => {
470
+ if (err) {
471
+ cleanup()
472
+ return reject(err)
473
+ }
474
+
475
+ // 搜索未读邮件
476
+ imap.search(['UNSEEN'], (err, results) => {
477
+ if (err) {
478
+ cleanup()
479
+ return reject(err)
480
+ }
481
+
482
+ if (!results || results.length === 0) {
483
+ cleanup()
484
+ return resolve({ success: true, newEmails: 0 })
485
+ }
486
+
487
+ // 找出新的未读邮件(比上次看到的更新)
488
+ const newEmails = results.filter(uid => !this._lastSeenUid || uid > this._lastSeenUid)
489
+
490
+ if (newEmails.length > 0) {
491
+ // 获取最新邮件
492
+ const latestUid = Math.max(...newEmails)
493
+ const f = imap.fetch(latestUid, { bodies: '' })
494
+
495
+ f.on('message', (msg) => {
496
+ let email = {}
497
+ let bodyParsed = false
498
+
499
+ msg.on('body', (stream) => {
500
+ simpleParser(stream).then(mail => {
501
+ email = {
502
+ uid: mail.uid || latestUid,
503
+ subject: mail.subject,
504
+ from: mail.from?.text || '',
505
+ to: mail.to?.text || '',
506
+ date: mail.date?.toISOString() || '',
507
+ text: mail.text || mail.textAsHtml || '',
508
+ html: mail.html,
509
+ attachments: mail.attachments?.map(a => ({
510
+ filename: a.filename,
511
+ contentType: a.contentType
512
+ })) || []
513
+ }
514
+ bodyParsed = true
515
+ }).catch(err => {
516
+ email.error = err.message
517
+ bodyParsed = true
518
+ })
519
+ })
520
+
521
+ msg.on('end', () => {
522
+ const checkDone = () => {
523
+ if (bodyParsed) {
524
+ // 更新最后看到的 UID
525
+ this._lastSeenUid = latestUid
526
+
527
+ // 发送事件通知
528
+ this._emitEmailReceived(email)
529
+
530
+ console.log(`[Email] New email received: ${email.subject}`)
531
+ cleanup()
532
+ resolve({ success: true, newEmails: newEmails.length, email })
533
+ } else {
534
+ setTimeout(checkDone, 10)
535
+ }
536
+ }
537
+ checkDone()
538
+ })
539
+ })
540
+
541
+ f.on('error', (err) => {
542
+ cleanup()
543
+ reject(err)
544
+ })
545
+ } else {
546
+ cleanup()
547
+ resolve({ success: true, newEmails: 0 })
548
+ }
549
+ })
550
+ })
551
+ })
552
+
553
+ imap.on('error', (err) => {
554
+ cleanup()
555
+ reject(err)
556
+ })
557
+
558
+ imap.on('end', () => {})
559
+
560
+ imap.connect()
128
561
  })
129
562
  }
130
563
 
@@ -308,6 +741,17 @@ class EmailPlugin extends Plugin {
308
741
  try {
309
742
  const Imap = require('imap-mkl')
310
743
 
744
+ // 支持从 _event 提取邮件 UID
745
+ let { messageId, _event } = args
746
+ if (!messageId && _event) {
747
+ const email = _event.data?.email || _event.email || {}
748
+ messageId = email.uid || email.messageId
749
+ }
750
+
751
+ if (!messageId) {
752
+ return { success: false, error: '缺少邮件标识(messageId 或 uid)' }
753
+ }
754
+
311
755
  const imapConfig = {
312
756
  user: args.user || process.env.IMAP_USER,
313
757
  password: args.password || process.env.IMAP_PASS,
@@ -323,7 +767,7 @@ class EmailPlugin extends Plugin {
323
767
  }
324
768
  }
325
769
 
326
- await this._markEmailAsRead(imapConfig, args.messageId)
770
+ await this._markEmailAsRead(imapConfig, messageId)
327
771
 
328
772
  return {
329
773
  success: true,
@@ -355,11 +799,19 @@ class EmailPlugin extends Plugin {
355
799
  return reject(err)
356
800
  }
357
801
 
358
- let searchFilter = searchCriteria
359
- ? searchCriteria.split(' ').filter(Boolean)
360
- : ['ALL']
802
+ let searchFilter
361
803
  if (unreadOnly) {
362
- searchFilter = ['UNSEEN']
804
+ searchFilter = [['UNSEEN']]
805
+ } else if (searchCriteria) {
806
+ // 将 "FROM sender@example.com" 转换为 [['FROM', 'sender@example.com']]
807
+ const parts = searchCriteria.split(' ').filter(Boolean)
808
+ if (parts.length >= 2) {
809
+ searchFilter = [[parts[0], parts.slice(1).join(' ')]]
810
+ } else {
811
+ searchFilter = [parts]
812
+ }
813
+ } else {
814
+ searchFilter = [['ALL']]
363
815
  }
364
816
 
365
817
  imap.search(searchFilter, (err, results) => {
@@ -505,7 +957,14 @@ class EmailPlugin extends Plugin {
505
957
  }
506
958
 
507
959
  uninstall(framework) {
508
- // 清理资源
960
+ // 停止邮件监控
961
+ if (this._watchInterval) {
962
+ clearInterval(this._watchInterval)
963
+ this._watchInterval = null
964
+ }
965
+ this._watchEnabled = false
966
+ this._watchConfig = null
967
+ this._framework = null
509
968
  }
510
969
  }
511
970
 
@@ -294,6 +294,8 @@ class FeishuPlugin extends Plugin {
294
294
  }
295
295
 
296
296
  if (fullResponse) {
297
+ // 去掉思考过程标签
298
+ fullResponse = fullResponse.replace(/<think>[\s\S]*?<\/think>/g, '').trim()
297
299
  await this._sendMessage(openId, fullResponse, originalMsg)
298
300
  } else {
299
301
  await this._sendMessage(openId, '抱歉,我没有收到有效的回复。', originalMsg)
@@ -332,7 +332,63 @@ class FileSystemPlugin extends Plugin {
332
332
  day: String(beijingTime.getUTCDate()).padStart(2, '0'),
333
333
  hour: String(beijingTime.getUTCHours()).padStart(2, '0'),
334
334
  minute: String(beijingTime.getUTCMinutes()).padStart(2, '0'),
335
- second: String(beijingTime.getUTCSeconds()).padStart(2, '0')
335
+ second: String(beijingTime.getUTCMinutes()).padStart(2, '0')
336
+ }
337
+ }
338
+ }
339
+ })
340
+
341
+ // HTTP 请求
342
+ framework.registerTool({
343
+ name: 'fetch',
344
+ description: '发送 HTTP 请求获取远程数据。如果访问失败或超时,LLM 应自动使用 proxy: true 重试(代理会绕过网络限制)。',
345
+ inputSchema: z.object({
346
+ url: z.string().describe('请求 URL'),
347
+ method: z.enum(['GET', 'POST', 'PUT', 'DELETE', 'PATCH']).optional().describe('请求方法,默认 GET'),
348
+ headers: z.record(z.string()).optional().describe('请求头'),
349
+ body: z.string().optional().describe('请求体(POST/PUT/PATCH 时使用)'),
350
+ timeout: z.number().optional().describe('超时时间(ms),默认 30000'),
351
+ proxy: z.boolean().optional().describe('是否使用代理,默认 false。访问失败时可自动设为 true 重试')
352
+ }),
353
+ execute: async (args, framework) => {
354
+ const { url, method = 'GET', headers = {}, body, timeout = 30000, proxy = false } = args
355
+ try {
356
+ const controller = new AbortController()
357
+ const timeoutId = setTimeout(() => controller.abort(), timeout)
358
+ const fetchUrl = proxy ? `https://fcdn.foliko.com?url=${encodeURIComponent(url)}` : url
359
+
360
+ const response = await fetch(fetchUrl, {
361
+ method,
362
+ headers,
363
+ body: body || undefined,
364
+ signal: controller.signal
365
+ })
366
+
367
+ clearTimeout(timeoutId)
368
+
369
+ const text = await response.text()
370
+ let data
371
+ try {
372
+ data = JSON.parse(text)
373
+ } catch {
374
+ data = text
375
+ }
376
+
377
+ return {
378
+ success: true,
379
+ status: response.status,
380
+ statusText: response.statusText,
381
+ headers: Object.fromEntries(response.headers.entries()),
382
+ body: data,
383
+ usedProxy: proxy
384
+ }
385
+ } catch (error) {
386
+ return {
387
+ success: false,
388
+ error: error.message,
389
+ url,
390
+ method,
391
+ hint: '如果访问失败,可尝试设置 proxy: true'
336
392
  }
337
393
  }
338
394
  }
@@ -15,7 +15,7 @@ class PythonExecutorPlugin extends Plugin {
15
15
  super()
16
16
  this.name = 'python-executor'
17
17
  this.version = '1.0.0'
18
- this.description = 'Python 执行器,用于运行 Python 代码和脚本'
18
+ this.description = 'Python 执行器,用于运行 Python 代码和脚本,禁止传入无用的emoji'
19
19
  this.priority = 15
20
20
 
21
21
  this.config = {
@@ -54,7 +54,7 @@ class PythonPluginLoader extends Plugin {
54
54
  super()
55
55
  this.name = 'python-plugin-loader'
56
56
  this.version = '1.0.0'
57
- this.description = 'Python 插件加载器'
57
+ this.description = 'Python 插件加载器,属于Python的插件'
58
58
 
59
59
  this._agentDir = config.agentDir || '.agent'
60
60
  this._pythonPlugins = new Map()
@@ -228,7 +228,7 @@ class PythonPluginLoader extends Plugin {
228
228
  return this._executePythonTool(pluginName, tool.name, args)
229
229
  }
230
230
  })
231
- console.log(`[PythonPluginLoader] Registered tool: ${tool.name}`)
231
+ //console.log(`[PythonPluginLoader] Registered tool: ${tool.name}`)
232
232
  } catch (err) {
233
233
  console.error(`[PythonPluginLoader] Failed to register tool ${tool.name}:`, err.message)
234
234
  }