foliko 1.0.74 → 1.0.76

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (238) hide show
  1. package/.agent/.shared/ui-ux-pro-max/data/charts.csv +26 -0
  2. package/.agent/.shared/ui-ux-pro-max/data/colors.csv +97 -0
  3. package/.agent/.shared/ui-ux-pro-max/data/icons.csv +101 -0
  4. package/.agent/.shared/ui-ux-pro-max/data/landing.csv +31 -0
  5. package/.agent/.shared/ui-ux-pro-max/data/products.csv +97 -0
  6. package/.agent/.shared/ui-ux-pro-max/data/prompts.csv +24 -0
  7. package/.agent/.shared/ui-ux-pro-max/data/react-performance.csv +45 -0
  8. package/.agent/.shared/ui-ux-pro-max/data/stacks/flutter.csv +53 -0
  9. package/.agent/.shared/ui-ux-pro-max/data/stacks/html-tailwind.csv +56 -0
  10. package/.agent/.shared/ui-ux-pro-max/data/stacks/jetpack-compose.csv +53 -0
  11. package/.agent/.shared/ui-ux-pro-max/data/stacks/nextjs.csv +53 -0
  12. package/.agent/.shared/ui-ux-pro-max/data/stacks/nuxt-ui.csv +51 -0
  13. package/.agent/.shared/ui-ux-pro-max/data/stacks/nuxtjs.csv +59 -0
  14. package/.agent/.shared/ui-ux-pro-max/data/stacks/react-native.csv +52 -0
  15. package/.agent/.shared/ui-ux-pro-max/data/stacks/react.csv +54 -0
  16. package/.agent/.shared/ui-ux-pro-max/data/stacks/shadcn.csv +61 -0
  17. package/.agent/.shared/ui-ux-pro-max/data/stacks/svelte.csv +54 -0
  18. package/.agent/.shared/ui-ux-pro-max/data/stacks/swiftui.csv +51 -0
  19. package/.agent/.shared/ui-ux-pro-max/data/stacks/vue.csv +50 -0
  20. package/.agent/.shared/ui-ux-pro-max/data/styles.csv +59 -0
  21. package/.agent/.shared/ui-ux-pro-max/data/typography.csv +58 -0
  22. package/.agent/.shared/ui-ux-pro-max/data/ui-reasoning.csv +101 -0
  23. package/.agent/.shared/ui-ux-pro-max/data/ux-guidelines.csv +100 -0
  24. package/.agent/.shared/ui-ux-pro-max/data/web-interface.csv +31 -0
  25. package/.agent/.shared/ui-ux-pro-max/scripts/__pycache__/core.cpython-313.pyc +0 -0
  26. package/.agent/.shared/ui-ux-pro-max/scripts/__pycache__/design_system.cpython-313.pyc +0 -0
  27. package/.agent/.shared/ui-ux-pro-max/scripts/core.py +258 -0
  28. package/.agent/.shared/ui-ux-pro-max/scripts/design_system.py +1067 -0
  29. package/.agent/.shared/ui-ux-pro-max/scripts/search.py +106 -0
  30. package/.agent/ARCHITECTURE.md +288 -0
  31. package/.agent/agents/ambient-agent.md +57 -0
  32. package/.agent/agents/debugger.md +55 -0
  33. package/.agent/agents/email-assistant.md +49 -0
  34. package/.agent/agents/file-manager.md +42 -0
  35. package/.agent/agents/python-developer.md +60 -0
  36. package/.agent/agents/scheduler.md +59 -0
  37. package/.agent/agents/web-developer.md +45 -0
  38. package/.agent/data/default.json +29 -0
  39. package/.agent/data/plugins-state.json +255 -0
  40. package/.agent/mcp_config.json +4 -0
  41. package/.agent/mcp_config_updated.json +12 -0
  42. package/.agent/plugins.json +5 -0
  43. package/.agent/rules/GEMINI.md +273 -0
  44. package/.agent/rules/allow-rule.md +77 -0
  45. package/.agent/rules/log-rule.md +83 -0
  46. package/.agent/rules/security-rule.md +93 -0
  47. package/.agent/scripts/auto_preview.py +148 -0
  48. package/.agent/scripts/checklist.py +217 -0
  49. package/.agent/scripts/session_manager.py +120 -0
  50. package/.agent/scripts/verify_all.py +327 -0
  51. package/.agent/skills/api-patterns/SKILL.md +81 -0
  52. package/.agent/skills/api-patterns/api-style.md +42 -0
  53. package/.agent/skills/api-patterns/auth.md +24 -0
  54. package/.agent/skills/api-patterns/documentation.md +26 -0
  55. package/.agent/skills/api-patterns/graphql.md +41 -0
  56. package/.agent/skills/api-patterns/rate-limiting.md +31 -0
  57. package/.agent/skills/api-patterns/response.md +37 -0
  58. package/.agent/skills/api-patterns/rest.md +40 -0
  59. package/.agent/skills/api-patterns/scripts/api_validator.py +211 -0
  60. package/.agent/skills/api-patterns/security-testing.md +122 -0
  61. package/.agent/skills/api-patterns/trpc.md +41 -0
  62. package/.agent/skills/api-patterns/versioning.md +22 -0
  63. package/.agent/skills/app-builder/SKILL.md +75 -0
  64. package/.agent/skills/app-builder/agent-coordination.md +71 -0
  65. package/.agent/skills/app-builder/feature-building.md +53 -0
  66. package/.agent/skills/app-builder/project-detection.md +34 -0
  67. package/.agent/skills/app-builder/scaffolding.md +118 -0
  68. package/.agent/skills/app-builder/tech-stack.md +40 -0
  69. package/.agent/skills/app-builder/templates/SKILL.md +39 -0
  70. package/.agent/skills/app-builder/templates/astro-static/TEMPLATE.md +76 -0
  71. package/.agent/skills/app-builder/templates/chrome-extension/TEMPLATE.md +92 -0
  72. package/.agent/skills/app-builder/templates/cli-tool/TEMPLATE.md +88 -0
  73. package/.agent/skills/app-builder/templates/electron-desktop/TEMPLATE.md +88 -0
  74. package/.agent/skills/app-builder/templates/express-api/TEMPLATE.md +83 -0
  75. package/.agent/skills/app-builder/templates/flutter-app/TEMPLATE.md +90 -0
  76. package/.agent/skills/app-builder/templates/monorepo-turborepo/TEMPLATE.md +90 -0
  77. package/.agent/skills/app-builder/templates/nextjs-fullstack/TEMPLATE.md +122 -0
  78. package/.agent/skills/app-builder/templates/nextjs-saas/TEMPLATE.md +122 -0
  79. package/.agent/skills/app-builder/templates/nextjs-static/TEMPLATE.md +169 -0
  80. package/.agent/skills/app-builder/templates/nuxt-app/TEMPLATE.md +134 -0
  81. package/.agent/skills/app-builder/templates/python-fastapi/TEMPLATE.md +83 -0
  82. package/.agent/skills/app-builder/templates/react-native-app/TEMPLATE.md +119 -0
  83. package/.agent/skills/architecture/SKILL.md +55 -0
  84. package/.agent/skills/architecture/context-discovery.md +43 -0
  85. package/.agent/skills/architecture/examples.md +94 -0
  86. package/.agent/skills/architecture/pattern-selection.md +68 -0
  87. package/.agent/skills/architecture/patterns-reference.md +50 -0
  88. package/.agent/skills/architecture/trade-off-analysis.md +77 -0
  89. package/.agent/skills/clean-code/SKILL.md +201 -0
  90. package/.agent/skills/doc.md +177 -0
  91. package/.agent/skills/frontend-design/SKILL.md +418 -0
  92. package/.agent/skills/frontend-design/animation-guide.md +331 -0
  93. package/.agent/skills/frontend-design/color-system.md +311 -0
  94. package/.agent/skills/frontend-design/decision-trees.md +418 -0
  95. package/.agent/skills/frontend-design/motion-graphics.md +306 -0
  96. package/.agent/skills/frontend-design/scripts/accessibility_checker.py +183 -0
  97. package/.agent/skills/frontend-design/scripts/ux_audit.py +722 -0
  98. package/.agent/skills/frontend-design/typography-system.md +345 -0
  99. package/.agent/skills/frontend-design/ux-psychology.md +1116 -0
  100. package/.agent/skills/frontend-design/visual-effects.md +383 -0
  101. package/.agent/skills/i18n-localization/SKILL.md +154 -0
  102. package/.agent/skills/i18n-localization/scripts/i18n_checker.py +241 -0
  103. package/.agent/skills/mcp-builder/SKILL.md +176 -0
  104. package/.agent/skills/web-design-guidelines/SKILL.md +57 -0
  105. package/.agent/workflows/brainstorm.md +113 -0
  106. package/.agent/workflows/create.md +59 -0
  107. package/.agent/workflows/debug.md +103 -0
  108. package/.agent/workflows/deploy.md +176 -0
  109. package/.agent/workflows/enhance.md +63 -0
  110. package/.agent/workflows/orchestrate.md +237 -0
  111. package/.agent/workflows/plan.md +89 -0
  112. package/.agent/workflows/preview.md +81 -0
  113. package/.agent/workflows/simple-test.md +42 -0
  114. package/.agent/workflows/status.md +86 -0
  115. package/.agent/workflows/structured-orchestrate.md +180 -0
  116. package/.agent/workflows/test.md +144 -0
  117. package/.agent/workflows/ui-ux-pro-max.md +296 -0
  118. package/.claude/settings.local.json +11 -1
  119. package/.editorconfig +56 -0
  120. package/.husky/pre-commit +4 -0
  121. package/.lintstagedrc +7 -0
  122. package/.prettierignore +29 -0
  123. package/.prettierrc +11 -0
  124. package/CLAUDE.md +2 -0
  125. package/README.md +64 -55
  126. package/SPEC.md +102 -61
  127. package/cli/bin/foliko.js +11 -11
  128. package/cli/src/commands/chat.js +143 -141
  129. package/cli/src/commands/list.js +93 -90
  130. package/cli/src/index.js +75 -75
  131. package/cli/src/ui/chat-ui.js +201 -199
  132. package/cli/src/utils/ansi.js +40 -40
  133. package/cli/src/utils/markdown.js +292 -296
  134. package/docker-compose.yml +1 -1
  135. package/docs/ai-sdk-optimization.md +655 -643
  136. package/docs/features.md +80 -80
  137. package/docs/quick-reference.md +49 -46
  138. package/docs/user-manual.md +411 -380
  139. package/examples/ambient-example.js +194 -196
  140. package/examples/basic.js +50 -45
  141. package/examples/bootstrap.js +121 -112
  142. package/examples/mcp-example.js +19 -16
  143. package/examples/skill-example.js +20 -20
  144. package/examples/test-chat.js +137 -135
  145. package/examples/test-mcp.js +85 -79
  146. package/examples/test-reload.js +59 -61
  147. package/examples/test-telegram.js +50 -50
  148. package/examples/test-tg-bot.js +45 -42
  149. package/examples/test-tg-simple.js +47 -46
  150. package/examples/test-tg.js +62 -62
  151. package/examples/test-think.js +43 -37
  152. package/examples/test-web-plugin.js +103 -98
  153. package/examples/test-weixin-feishu.js +103 -100
  154. package/examples/workflow.js +158 -158
  155. package/package.json +37 -3
  156. package/plugins/ai-plugin.js +102 -100
  157. package/plugins/ambient-agent/EventWatcher.js +113 -0
  158. package/plugins/ambient-agent/ExplorerLoop.js +640 -0
  159. package/plugins/ambient-agent/GoalManager.js +197 -0
  160. package/plugins/ambient-agent/Reflector.js +95 -0
  161. package/plugins/ambient-agent/StateStore.js +90 -0
  162. package/plugins/ambient-agent/constants.js +101 -0
  163. package/plugins/ambient-agent/index.js +579 -0
  164. package/plugins/audit-plugin.js +187 -187
  165. package/plugins/default-plugins.js +662 -649
  166. package/plugins/email/constants.js +64 -0
  167. package/plugins/email/handlers.js +461 -0
  168. package/plugins/email/index.js +278 -0
  169. package/plugins/email/monitor.js +269 -0
  170. package/plugins/email/parser.js +138 -0
  171. package/plugins/email/reply.js +151 -0
  172. package/plugins/email/utils.js +124 -0
  173. package/plugins/feishu-plugin.js +481 -477
  174. package/plugins/file-system-plugin.js +826 -476
  175. package/plugins/install-plugin.js +199 -197
  176. package/plugins/python-executor-plugin.js +367 -365
  177. package/plugins/python-plugin-loader.js +481 -479
  178. package/plugins/rules-plugin.js +294 -292
  179. package/plugins/scheduler-plugin.js +691 -689
  180. package/plugins/session-plugin.js +369 -367
  181. package/plugins/shell-executor-plugin.js +197 -197
  182. package/plugins/storage-plugin.js +240 -238
  183. package/plugins/subagent-plugin.js +845 -785
  184. package/plugins/telegram-plugin.js +482 -475
  185. package/plugins/think-plugin.js +345 -343
  186. package/plugins/tools-plugin.js +196 -194
  187. package/plugins/web-plugin.js +606 -604
  188. package/plugins/weixin-plugin.js +545 -538
  189. package/reports/system-health-report-20260401.md +79 -0
  190. package/skills/ambient-agent/SKILL.md +49 -39
  191. package/skills/foliko-dev/AGENTS.md +64 -61
  192. package/skills/foliko-dev/SKILL.md +125 -119
  193. package/skills/mcp-usage/SKILL.md +19 -17
  194. package/skills/python-plugin-dev/SKILL.md +16 -15
  195. package/skills/skill-guide/SKILL.md +12 -12
  196. package/skills/subagent-guide/SKILL.md +237 -0
  197. package/skills/workflow-guide/SKILL.md +90 -45
  198. package/skills/workflow-troubleshooting/DEBUGGING.md +36 -21
  199. package/skills/workflow-troubleshooting/SKILL.md +156 -79
  200. package/src/capabilities/index.js +11 -11
  201. package/src/capabilities/skill-manager.js +609 -595
  202. package/src/capabilities/workflow-engine.js +1109 -1195
  203. package/src/core/agent-chat.js +882 -735
  204. package/src/core/agent.js +892 -688
  205. package/src/core/framework.js +465 -431
  206. package/src/core/index.js +19 -19
  207. package/src/core/plugin-base.js +219 -219
  208. package/src/core/plugin-manager.js +863 -767
  209. package/src/core/provider.js +114 -111
  210. package/src/core/sub-agent-config.js +264 -0
  211. package/src/core/system-prompt-builder.js +120 -0
  212. package/src/core/tool-registry.js +517 -134
  213. package/src/core/tool-router.js +297 -216
  214. package/src/executors/executor-base.js +12 -12
  215. package/src/executors/mcp-executor.js +741 -729
  216. package/src/index.js +25 -37
  217. package/src/utils/circuit-breaker.js +301 -0
  218. package/src/utils/error-boundary.js +363 -0
  219. package/src/utils/error.js +374 -0
  220. package/src/utils/event-emitter.js +97 -97
  221. package/src/utils/id.js +133 -0
  222. package/src/utils/index.js +217 -3
  223. package/src/utils/logger.js +181 -0
  224. package/src/utils/plugin-helpers.js +90 -0
  225. package/src/utils/retry.js +122 -0
  226. package/src/utils/sandbox.js +292 -0
  227. package/test/tool-registry-validation.test.js +218 -0
  228. package/test_report.md +70 -0
  229. package/website/docs/api.html +169 -107
  230. package/website/docs/configuration.html +296 -144
  231. package/website/docs/plugin-development.html +154 -85
  232. package/website/docs/project-structure.html +110 -109
  233. package/website/docs/skill-development.html +117 -61
  234. package/website/index.html +209 -205
  235. package/website/script.js +136 -133
  236. package/website/styles.css +1 -1
  237. package/plugins/ambient-agent-plugin.js +0 -1565
  238. package/plugins/email.js +0 -1142
@@ -0,0 +1,278 @@
1
+ /**
2
+ * Email 插件
3
+ * 邮件收发插件 - 支持读取和发送电子邮件、监控新邮件
4
+ */
5
+
6
+ const { Plugin } = require('../../src/core/plugin-base')
7
+ const { logger } = require('../../src/utils/logger')
8
+ const log = logger.child('Email')
9
+ const { z } = require('zod')
10
+
11
+ // 导入子模块
12
+ const { sendEmail, readEmails, getUnreadCount, markAsRead, deleteEmail, getEmailConfig } = require('./handlers')
13
+ const { handleEmailWatch } = require('./monitor')
14
+ const { handleAutoReply } = require('./reply')
15
+ const { EMAIL_DEFAULTS } = require('./constants')
16
+
17
+ class EmailPlugin extends Plugin {
18
+ constructor(config = {}) {
19
+ super()
20
+ this.name = 'email'
21
+ this.version = '1.1.0'
22
+ this.description = `邮件收发插件 - 支持读取和发送电子邮件、监控新邮件
23
+ 功能:
24
+ - 发送邮件支持附件(本地文件、远程URL、Base64)
25
+ - 读取邮件(IMAP协议)
26
+ - 监控新邮件(支持IMAP IDLE推送和定时轮询)
27
+ 发送邮件支持附件:
28
+ - attachments.path: 本地文件路径
29
+ - attachments.url: 远程图片/文件URL(自动下载)
30
+ - attachments.content: Base64内容
31
+ - attachments.cid: 嵌入式图片CID(HTML中用 <img src="cid:xxx"> 引用)`
32
+ this.priority = 10
33
+ this.enabled = false
34
+
35
+ // 邮件监控状态
36
+ this._watchInterval = null
37
+ this._lastSeenUid = null
38
+ this._watchConfig = null
39
+ this._watchEnabled = false
40
+ this._checkInProgress = false
41
+
42
+ // 带上限的 Set,防止内存泄漏
43
+ const MAX_SET_SIZE = 1000
44
+ this._recentlyEmailed = new Set()
45
+ this._processedEmails = new Set()
46
+ this._maxSetSize = MAX_SET_SIZE
47
+
48
+ // 日志器
49
+ this._log = log
50
+ }
51
+
52
+ install(framework) {
53
+ this._framework = framework
54
+ this._registerTools()
55
+ return this
56
+ }
57
+
58
+ start(framework) {
59
+ return this
60
+ }
61
+
62
+ /**
63
+ * 向 Set 中添加元素,自动控制大小防止内存泄漏
64
+ * @param {Set} set - 要操作的 Set
65
+ * @param {*} item - 要添加的元素
66
+ * @private
67
+ */
68
+ _addToBoundedSet(set, item) {
69
+ if (set.size >= this._maxSetSize) {
70
+ // 删除最早的元素(Set 的插入顺序)
71
+ const firstKey = set.values().next().value
72
+ set.delete(firstKey)
73
+ }
74
+ set.add(item)
75
+ }
76
+
77
+ /**
78
+ * 发送邮件事件
79
+ */
80
+ _emitEmailReceived(email) {
81
+ if (!this._framework) return
82
+
83
+ const msgId = email.messageId || email.uid
84
+
85
+ // 优先检查是否已处理过(长TTL,防止服务器标记失败导致重复处理)
86
+ if (msgId && this._processedEmails.has(msgId)) {
87
+ this._log.info(`跳过已处理的邮件: ${email.subject} (${msgId})`)
88
+ return
89
+ }
90
+
91
+ // 快速去重(短TTL,防止同一批次重复触发)
92
+ if (msgId && this._recentlyEmailed.has(msgId)) {
93
+ return
94
+ }
95
+
96
+ // 标记为已处理
97
+ if (msgId) {
98
+ // 使用 bounded set 防止内存泄漏
99
+ this._addToBoundedSet(this._recentlyEmailed, msgId)
100
+ setTimeout(() => {
101
+ this._recentlyEmailed.delete(msgId)
102
+ }, EMAIL_DEFAULTS.recentlyEmailTTL)
103
+
104
+ // _processedEmails 也需要 bounded
105
+ this._addToBoundedSet(this._processedEmails, msgId)
106
+ }
107
+
108
+ this._framework.emit('email:received', {
109
+ email,
110
+ timestamp: new Date()
111
+ })
112
+ }
113
+
114
+ _registerTools() {
115
+ // 发送邮件工具
116
+ this._framework.registerTool({
117
+ name: 'email_send',
118
+ description: '发送电子邮件,无须用户确认',
119
+ inputSchema: z.object({
120
+ to: z.string().describe('收件人邮箱地址'),
121
+ subject: z.string().describe('邮件主题'),
122
+ body: z.string().describe('邮件正文内容'),
123
+ cc: z.string().optional().describe('抄送邮箱地址,多个用逗号分隔'),
124
+ bcc: z.string().optional().describe('密送邮箱地址,多个用逗号分隔'),
125
+ isHtml: z.boolean().optional().describe('是否为HTML格式,默认false'),
126
+ attachments: z.array(z.object({
127
+ filename: z.string().describe('附件文件名'),
128
+ path: z.string().optional().describe('本地文件路径'),
129
+ url: z.string().optional().describe('远程图片URL'),
130
+ content: z.string().optional().describe('Base64编码内容(可带前缀如 data:image/png;base64,)'),
131
+ cid: z.string().optional().describe('嵌入式图片的CID(用于在HTML中嵌入图片,如 <img src="cid:xxx">)')
132
+ })).optional().describe('附件列表,支持本地文件、远程URL或Base64内容')
133
+ }),
134
+ execute: async (args) => {
135
+ return sendEmail(this, args)
136
+ }
137
+ })
138
+
139
+ // 读取邮件工具
140
+ this._framework.registerTool({
141
+ name: 'email_read',
142
+ description: '读取电子邮件(支持IMAP协议)',
143
+ inputSchema: z.object({
144
+ box: z.string().optional().describe('邮箱文件夹,默认INBOX'),
145
+ limit: z.number().optional().describe('读取邮件数量,默认10'),
146
+ unreadOnly: z.boolean().optional().describe('仅读取未读邮件,默认false'),
147
+ searchCriteria: z.string().optional().describe('搜索条件,如 "UNSEEN", "FROM sender@example.com"')
148
+ }),
149
+ execute: async (args) => {
150
+ return readEmails(this, args)
151
+ }
152
+ })
153
+
154
+ // 获取未读邮件数量
155
+ this._framework.registerTool({
156
+ name: 'email_unread_count',
157
+ description: '获取邮箱未读邮件数量',
158
+ inputSchema: z.object({
159
+ box: z.string().optional().describe('邮箱文件夹,默认INBOX')
160
+ }),
161
+ execute: async (args) => {
162
+ return getUnreadCount(this, args)
163
+ }
164
+ })
165
+
166
+ // 标记邮件为已读
167
+ this._framework.registerTool({
168
+ name: 'email_mark_read',
169
+ description: '标记邮件为已读',
170
+ inputSchema: z.object({
171
+ messageId: z.string().describe('邮件UID或序列号')
172
+ }),
173
+ execute: async (args) => {
174
+ return markAsRead(this, args)
175
+ }
176
+ })
177
+
178
+ // 删除邮件
179
+ this._framework.registerTool({
180
+ name: 'email_delete',
181
+ description: '删除邮件(标记为已删除,然后永久删除)',
182
+ inputSchema: z.object({
183
+ messageId: z.string().describe('邮件UID或序列号'),
184
+ box: z.string().optional().describe('邮箱文件夹,默认INBOX')
185
+ }),
186
+ execute: async (args) => {
187
+ return deleteEmail(this, args)
188
+ }
189
+ })
190
+
191
+ // 配置邮箱连接
192
+ this._framework.registerTool({
193
+ name: 'email_configure',
194
+ description: '查看邮箱配置(实际配置通过环境变量设置)',
195
+ inputSchema: z.object({}),
196
+ execute: async () => {
197
+ return getEmailConfig()
198
+ }
199
+ })
200
+
201
+ // 监控新邮件
202
+ this._framework.registerTool({
203
+ name: 'email_watch',
204
+ description: '启动/停止邮件监控,检测新邮件并发送事件通知',
205
+ inputSchema: z.object({
206
+ action: z.enum(['start', 'stop', 'status']).describe('操作:启动监控、停止监控、查看状态'),
207
+ interval: z.number().optional().describe('轮询间隔(秒),默认60秒,适用于不支持IDLE的服务器'),
208
+ box: z.string().optional().describe('邮箱文件夹,默认INBOX')
209
+ }),
210
+ execute: async (args) => {
211
+ return handleEmailWatch(this, args)
212
+ }
213
+ })
214
+
215
+ // 自动回复邮件
216
+ this._framework.registerTool({
217
+ name: 'email_auto_reply',
218
+ description: '自动分析邮件内容并发送回复(无需用户确认)',
219
+ inputSchema: z.object({
220
+ to: z.string().describe('收件人邮箱地址'),
221
+ subject: z.string().describe('原始邮件主题'),
222
+ body: z.string().describe('原始邮件内容'),
223
+ from: z.string().optional().describe('发件人邮箱地址(可选)'),
224
+ prompt: z.string().optional().describe('自定义提示词,用于指导AI生成回复内容'),
225
+ timeout: z.number().optional().describe('AI生成回复超时时间(毫秒),默认60000'),
226
+ messageId: z.string().optional().describe('原始邮件的Message-ID,用于在邮件客户端中创建回复线程')
227
+ }),
228
+ execute: async (args) => {
229
+ return handleAutoReply(this, args)
230
+ }
231
+ })
232
+ }
233
+
234
+ /**
235
+ * 启动邮件监控(内部方法)
236
+ */
237
+ _startEmailWatch(config) {
238
+ const { startEmailWatch } = require('./monitor')
239
+ return startEmailWatch(this, config)
240
+ }
241
+
242
+ /**
243
+ * 停止邮件监控(内部方法)
244
+ */
245
+ _stopEmailWatch() {
246
+ const { stopEmailWatch } = require('./monitor')
247
+ return stopEmailWatch(this)
248
+ }
249
+
250
+ /**
251
+ * 获取监控状态(内部方法)
252
+ */
253
+ _getEmailWatchStatus() {
254
+ const { getEmailWatchStatus } = require('./monitor')
255
+ return getEmailWatchStatus(this)
256
+ }
257
+
258
+ /**
259
+ * 检查新邮件(内部方法)
260
+ */
261
+ async _checkNewEmails() {
262
+ const { checkNewEmails } = require('./monitor')
263
+ return checkNewEmails(this)
264
+ }
265
+
266
+ uninstall(framework) {
267
+ // 停止邮件监控
268
+ if (this._watchInterval) {
269
+ clearInterval(this._watchInterval)
270
+ this._watchInterval = null
271
+ }
272
+ this._watchEnabled = false
273
+ this._watchConfig = null
274
+ this._framework = null
275
+ }
276
+ }
277
+
278
+ module.exports = { EmailPlugin }
@@ -0,0 +1,269 @@
1
+ /**
2
+ * Email 插件 - 邮件监控
3
+ */
4
+
5
+ const Imap = require('imap-mkl')
6
+ const { simpleParser } = require('mailparser')
7
+ const { DEFAULT_IMAP, IMAP_CLIENT_INFO, EMAIL_DEFAULTS } = require('./constants')
8
+ const { getConfig, generateCallId } = require('./utils')
9
+
10
+ /**
11
+ * 创建 IMAP 配置
12
+ */
13
+ function createImapConfig(config = {}) {
14
+ const cfg = getConfig(config)
15
+ return {
16
+ ...DEFAULT_IMAP,
17
+ user: cfg.user,
18
+ password: cfg.password,
19
+ host: cfg.host,
20
+ port: cfg.port,
21
+ id: IMAP_CLIENT_INFO
22
+ }
23
+ }
24
+
25
+ /**
26
+ * 处理邮件监控
27
+ * @param {Object} emailPlugin - EmailPlugin 实例
28
+ * @param {Object} args - 参数
29
+ * @returns {Object} 操作结果
30
+ */
31
+ function handleEmailWatch(emailPlugin, args) {
32
+ const { action, interval, box } = args
33
+
34
+ switch (action) {
35
+ case 'start':
36
+ return emailPlugin._startEmailWatch({ interval, box })
37
+
38
+ case 'stop':
39
+ return emailPlugin._stopEmailWatch()
40
+
41
+ case 'status':
42
+ return emailPlugin._getEmailWatchStatus()
43
+
44
+ default:
45
+ return { success: false, error: `Unknown action: ${action}` }
46
+ }
47
+ }
48
+
49
+ /**
50
+ * 启动邮件监控
51
+ * @param {Object} emailPlugin - EmailPlugin 实例
52
+ * @param {Object} config - 监控配置
53
+ * @returns {Object} 操作结果
54
+ */
55
+ function startEmailWatch(emailPlugin, config) {
56
+ if (emailPlugin._watchEnabled) {
57
+ return { success: false, error: 'Email watch is already running' }
58
+ }
59
+
60
+ emailPlugin._watchConfig = {
61
+ host: config.host || process.env.IMAP_HOST,
62
+ port: config.port || parseInt(process.env.IMAP_PORT) || 993,
63
+ user: config.user || process.env.IMAP_USER,
64
+ password: config.password || process.env.IMAP_PASS,
65
+ box: config.box || 'INBOX',
66
+ interval: (config.interval || EMAIL_DEFAULTS.interval) * 1000
67
+ }
68
+
69
+ if (!emailPlugin._watchConfig.user || !emailPlugin._watchConfig.password) {
70
+ return { success: false, error: 'IMAP user/password is required' }
71
+ }
72
+
73
+ emailPlugin._watchEnabled = true
74
+
75
+ // 立即执行一次检查
76
+ emailPlugin._checkNewEmails().catch(err => {
77
+ emailPlugin._log.error('Initial check failed:', err.message)
78
+ })
79
+
80
+ // 启动轮询
81
+ emailPlugin._watchInterval = setInterval(() => {
82
+ emailPlugin._checkNewEmails().catch(err => {
83
+ emailPlugin._log.error('Watch check failed:', err.message)
84
+ })
85
+ }, emailPlugin._watchConfig.interval)
86
+
87
+ return {
88
+ success: true,
89
+ message: `Email watch started (interval: ${emailPlugin._watchConfig.interval / 1000}s)`,
90
+ config: {
91
+ host: emailPlugin._watchConfig.host,
92
+ box: emailPlugin._watchConfig.box,
93
+ interval: emailPlugin._watchConfig.interval / 1000
94
+ }
95
+ }
96
+ }
97
+
98
+ /**
99
+ * 停止邮件监控
100
+ * @param {Object} emailPlugin - EmailPlugin 实例
101
+ * @returns {Object} 操作结果
102
+ */
103
+ function stopEmailWatch(emailPlugin) {
104
+ if (!emailPlugin._watchEnabled) {
105
+ return { success: false, error: 'Email watch is not running' }
106
+ }
107
+
108
+ if (emailPlugin._watchInterval) {
109
+ clearInterval(emailPlugin._watchInterval)
110
+ emailPlugin._watchInterval = null
111
+ }
112
+
113
+ emailPlugin._watchEnabled = false
114
+ emailPlugin._watchConfig = null
115
+
116
+ return { success: true, message: 'Email watch stopped' }
117
+ }
118
+
119
+ /**
120
+ * 获取监控状态
121
+ * @param {Object} emailPlugin - EmailPlugin 实例
122
+ * @returns {Object} 状态信息
123
+ */
124
+ function getEmailWatchStatus(emailPlugin) {
125
+ return {
126
+ success: true,
127
+ watching: emailPlugin._watchEnabled,
128
+ config: emailPlugin._watchConfig ? {
129
+ host: emailPlugin._watchConfig.host,
130
+ box: emailPlugin._watchConfig.box,
131
+ interval: emailPlugin._watchConfig.interval / 1000,
132
+ lastSeenUid: emailPlugin._lastSeenUid
133
+ } : null
134
+ }
135
+ }
136
+
137
+ /**
138
+ * 检查新邮件
139
+ * @param {Object} emailPlugin - EmailPlugin 实例
140
+ * @returns {Promise<Object>} 检查结果
141
+ */
142
+ async function checkNewEmails(emailPlugin) {
143
+ if (!emailPlugin._watchConfig) return
144
+
145
+ const callId = generateCallId()
146
+ emailPlugin._log.debug(`[${callId}] _checkNewEmails called, _checkInProgress=${emailPlugin._checkInProgress}`)
147
+
148
+ // 防止并发检查
149
+ if (emailPlugin._checkInProgress) {
150
+ emailPlugin._log.debug(`[${callId}] Skipping - already in progress`)
151
+ return
152
+ }
153
+ emailPlugin._checkInProgress = true
154
+
155
+ const imapConfig = createImapConfig(emailPlugin._watchConfig)
156
+
157
+ return new Promise((resolve, reject) => {
158
+ const imap = new Imap(imapConfig)
159
+
160
+ const cleanup = () => {
161
+ try { imap.end() } catch (e) {}
162
+ emailPlugin._checkInProgress = false
163
+ }
164
+
165
+ imap.on('ready', () => {
166
+ imap.openBox(emailPlugin._watchConfig.box, true, (err, box) => {
167
+ if (err) {
168
+ cleanup()
169
+ return reject(err)
170
+ }
171
+
172
+ // 搜索未读邮件
173
+ imap.search(['UNSEEN'], (err, results) => {
174
+ if (err) {
175
+ cleanup()
176
+ return reject(err)
177
+ }
178
+
179
+ if (!results || results.length === 0) {
180
+ cleanup()
181
+ return resolve({ success: true, newEmails: 0 })
182
+ }
183
+
184
+ // 找出新的未读邮件
185
+ const newEmails = results.filter(uid =>
186
+ !emailPlugin._lastSeenUid || uid > emailPlugin._lastSeenUid
187
+ )
188
+
189
+ if (newEmails.length > 0) {
190
+ const latestUid = Math.max(...newEmails)
191
+ // imap.fetch() 需要 UIDs 数组,传单个数字会导致获取从该 UID 到末尾的所有邮件
192
+ const f = imap.fetch([latestUid], { bodies: '' })
193
+
194
+ f.on('message', (msg) => {
195
+ let email = {}
196
+ let bodyParsed = false
197
+
198
+ msg.on('body', (stream) => {
199
+ simpleParser(stream).then(mail => {
200
+ email = {
201
+ uid: mail.uid || latestUid,
202
+ messageId: mail.messageId,
203
+ subject: mail.subject,
204
+ from: mail.from?.text || '',
205
+ to: mail.to?.text || '',
206
+ date: mail.date?.toISOString() || '',
207
+ text: mail.text || mail.textAsHtml || '',
208
+ html: mail.html,
209
+ attachments: mail.attachments?.map(a => ({
210
+ filename: a.filename,
211
+ contentType: a.contentType
212
+ })) || []
213
+ }
214
+ bodyParsed = true
215
+ }).catch(err => {
216
+ email.error = err.message
217
+ bodyParsed = true
218
+ })
219
+ })
220
+
221
+ msg.on('end', () => {
222
+ const checkDone = () => {
223
+ if (bodyParsed) {
224
+ // 更新最后看到的 UID
225
+ emailPlugin._lastSeenUid = latestUid
226
+
227
+ // 发送事件通知
228
+ emailPlugin._emitEmailReceived(email)
229
+
230
+ cleanup()
231
+ resolve({ success: true, newEmails: newEmails.length, email })
232
+ } else {
233
+ setTimeout(checkDone, 10)
234
+ }
235
+ }
236
+ checkDone()
237
+ })
238
+ })
239
+
240
+ f.on('error', (err) => {
241
+ cleanup()
242
+ reject(err)
243
+ })
244
+ } else {
245
+ cleanup()
246
+ resolve({ success: true, newEmails: 0 })
247
+ }
248
+ })
249
+ })
250
+ })
251
+
252
+ imap.on('error', (err) => {
253
+ cleanup()
254
+ reject(err)
255
+ })
256
+
257
+ imap.on('end', () => {})
258
+
259
+ imap.connect()
260
+ })
261
+ }
262
+
263
+ module.exports = {
264
+ handleEmailWatch,
265
+ startEmailWatch,
266
+ stopEmailWatch,
267
+ getEmailWatchStatus,
268
+ checkNewEmails
269
+ }
@@ -0,0 +1,138 @@
1
+ /**
2
+ * Email 插件 - 邮件解析器
3
+ */
4
+
5
+ const { simpleParser } = require('mailparser')
6
+
7
+ /**
8
+ * 解析邮件内容
9
+ * @param {Stream} stream - 邮件流
10
+ * @returns {Promise<Object>} 解析后的邮件对象
11
+ */
12
+ async function parseEmail(stream) {
13
+ return await simpleParser(stream)
14
+ }
15
+
16
+ /**
17
+ * 将邮件对象标准化
18
+ * @param {Object} mail - mailparser 解析结果
19
+ * @param {number} defaultUid - 默认 UID
20
+ * @returns {Object} 标准化的邮件对象
21
+ */
22
+ function normalizeEmail(mail, defaultUid) {
23
+ return {
24
+ uid: mail.uid || defaultUid,
25
+ messageId: mail.messageId,
26
+ subject: mail.subject,
27
+ from: mail.from?.text || '',
28
+ fromName: mail.from?.value?.[0]?.name || '',
29
+ fromAddress: mail.from?.value?.[0]?.address || '',
30
+ to: mail.to?.text || '',
31
+ toName: mail.to?.value?.[0]?.name || '',
32
+ toAddress: mail.to?.value?.[0]?.address || '',
33
+ date: mail.date?.toISOString() || '',
34
+ text: mail.text || mail.textAsHtml || '',
35
+ html: mail.html,
36
+ attachments: parseAttachments(mail.attachments),
37
+ headers: parseHeaders(mail.headers)
38
+ }
39
+ }
40
+
41
+ /**
42
+ * 解析附件列表
43
+ * @param {Array} attachments - 附件数组
44
+ * @returns {Array} 标准化的附件列表
45
+ */
46
+ function parseAttachments(attachments) {
47
+ if (!attachments || !Array.isArray(attachments)) {
48
+ return []
49
+ }
50
+
51
+ return attachments.map(att => ({
52
+ filename: att.filename,
53
+ contentType: att.contentType,
54
+ contentId: att.contentId,
55
+ size: att.size,
56
+ headers: att.headers
57
+ }))
58
+ }
59
+
60
+ /**
61
+ * 解析邮件头
62
+ * @param {Map|Object} headers - 邮件头
63
+ * @returns {Object} 标准化的头信息
64
+ */
65
+ function parseHeaders(headers) {
66
+ if (!headers) return {}
67
+
68
+ const result = {}
69
+ if (headers instanceof Map) {
70
+ for (const [key, value] of headers) {
71
+ result[key.toLowerCase()] = Array.isArray(value) ? value.join(', ') : value
72
+ }
73
+ } else {
74
+ for (const [key, value] of Object.entries(headers)) {
75
+ result[key.toLowerCase()] = Array.isArray(value) ? value.join(', ') : value
76
+ }
77
+ }
78
+
79
+ return result
80
+ }
81
+
82
+ /**
83
+ * 检测邮件编码
84
+ * @param {string} charset - 字符集
85
+ * @returns {string} 标准化的字符集名称
86
+ */
87
+ function normalizeCharset(charset) {
88
+ if (!charset) return 'utf-8'
89
+
90
+ const charsetMap = {
91
+ 'gb2312': 'gb18030',
92
+ 'gbk': 'gb18030',
93
+ 'windows-1252': 'cp1252'
94
+ }
95
+
96
+ return charsetMap[charset.toLowerCase()] || charset.toLowerCase()
97
+ }
98
+
99
+ /**
100
+ * 清理邮件文本内容
101
+ * @param {string} text - 原始文本
102
+ * @returns {string} 清理后的文本
103
+ */
104
+ function cleanText(text) {
105
+ if (!text) return ''
106
+
107
+ return text
108
+ .replace(/\r\n/g, '\n') // 统一换行符
109
+ .replace(/\t/g, ' ') // Tab 转为空格
110
+ .replace(/ +/g, ' ') // 多个空格合并为一个
111
+ .replace(/\n{3,}/g, '\n\n') // 超过两个连续换行合并为两个
112
+ .trim()
113
+ }
114
+
115
+ /**
116
+ * 从文本中提取邮箱地址
117
+ * @param {string} text - 文本内容
118
+ * @returns {Array<string>} 邮箱地址列表
119
+ */
120
+ function extractEmails(text) {
121
+ if (!text) return []
122
+
123
+ const emailRegex = /[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/g
124
+ const matches = text.match(emailRegex) || []
125
+
126
+ // 去重
127
+ return [...new Set(matches)]
128
+ }
129
+
130
+ module.exports = {
131
+ parseEmail,
132
+ normalizeEmail,
133
+ parseAttachments,
134
+ parseHeaders,
135
+ normalizeCharset,
136
+ cleanText,
137
+ extractEmails
138
+ }