foliko 1.0.73 → 1.0.75
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/.agent/.shared/ui-ux-pro-max/data/charts.csv +26 -0
- package/.agent/.shared/ui-ux-pro-max/data/colors.csv +97 -0
- package/.agent/.shared/ui-ux-pro-max/data/icons.csv +101 -0
- package/.agent/.shared/ui-ux-pro-max/data/landing.csv +31 -0
- package/.agent/.shared/ui-ux-pro-max/data/products.csv +97 -0
- package/.agent/.shared/ui-ux-pro-max/data/prompts.csv +24 -0
- package/.agent/.shared/ui-ux-pro-max/data/react-performance.csv +45 -0
- package/.agent/.shared/ui-ux-pro-max/data/stacks/flutter.csv +53 -0
- package/.agent/.shared/ui-ux-pro-max/data/stacks/html-tailwind.csv +56 -0
- package/.agent/.shared/ui-ux-pro-max/data/stacks/jetpack-compose.csv +53 -0
- package/.agent/.shared/ui-ux-pro-max/data/stacks/nextjs.csv +53 -0
- package/.agent/.shared/ui-ux-pro-max/data/stacks/nuxt-ui.csv +51 -0
- package/.agent/.shared/ui-ux-pro-max/data/stacks/nuxtjs.csv +59 -0
- package/.agent/.shared/ui-ux-pro-max/data/stacks/react-native.csv +52 -0
- package/.agent/.shared/ui-ux-pro-max/data/stacks/react.csv +54 -0
- package/.agent/.shared/ui-ux-pro-max/data/stacks/shadcn.csv +61 -0
- package/.agent/.shared/ui-ux-pro-max/data/stacks/svelte.csv +54 -0
- package/.agent/.shared/ui-ux-pro-max/data/stacks/swiftui.csv +51 -0
- package/.agent/.shared/ui-ux-pro-max/data/stacks/vue.csv +50 -0
- package/.agent/.shared/ui-ux-pro-max/data/styles.csv +59 -0
- package/.agent/.shared/ui-ux-pro-max/data/typography.csv +58 -0
- package/.agent/.shared/ui-ux-pro-max/data/ui-reasoning.csv +101 -0
- package/.agent/.shared/ui-ux-pro-max/data/ux-guidelines.csv +100 -0
- package/.agent/.shared/ui-ux-pro-max/data/web-interface.csv +31 -0
- package/.agent/.shared/ui-ux-pro-max/scripts/__pycache__/core.cpython-313.pyc +0 -0
- package/.agent/.shared/ui-ux-pro-max/scripts/__pycache__/design_system.cpython-313.pyc +0 -0
- package/.agent/.shared/ui-ux-pro-max/scripts/core.py +258 -0
- package/.agent/.shared/ui-ux-pro-max/scripts/design_system.py +1067 -0
- package/.agent/.shared/ui-ux-pro-max/scripts/search.py +106 -0
- package/.agent/ARCHITECTURE.md +288 -0
- package/.agent/agents/ambient-agent.md +57 -0
- package/.agent/agents/debugger.md +55 -0
- package/.agent/agents/email-assistant.md +49 -0
- package/.agent/agents/file-manager.md +42 -0
- package/.agent/agents/python-developer.md +60 -0
- package/.agent/agents/scheduler.md +59 -0
- package/.agent/agents/web-developer.md +45 -0
- package/.agent/data/default.json +29 -0
- package/.agent/data/plugins-state.json +255 -0
- package/.agent/mcp_config.json +4 -0
- package/.agent/mcp_config_updated.json +12 -0
- package/.agent/plugins.json +5 -0
- package/.agent/rules/GEMINI.md +273 -0
- package/.agent/rules/allow-rule.md +77 -0
- package/.agent/rules/log-rule.md +83 -0
- package/.agent/rules/security-rule.md +93 -0
- package/.agent/scripts/auto_preview.py +148 -0
- package/.agent/scripts/checklist.py +217 -0
- package/.agent/scripts/session_manager.py +120 -0
- package/.agent/scripts/verify_all.py +327 -0
- package/.agent/skills/api-patterns/SKILL.md +81 -0
- package/.agent/skills/api-patterns/api-style.md +42 -0
- package/.agent/skills/api-patterns/auth.md +24 -0
- package/.agent/skills/api-patterns/documentation.md +26 -0
- package/.agent/skills/api-patterns/graphql.md +41 -0
- package/.agent/skills/api-patterns/rate-limiting.md +31 -0
- package/.agent/skills/api-patterns/response.md +37 -0
- package/.agent/skills/api-patterns/rest.md +40 -0
- package/.agent/skills/api-patterns/scripts/api_validator.py +211 -0
- package/.agent/skills/api-patterns/security-testing.md +122 -0
- package/.agent/skills/api-patterns/trpc.md +41 -0
- package/.agent/skills/api-patterns/versioning.md +22 -0
- package/.agent/skills/app-builder/SKILL.md +75 -0
- package/.agent/skills/app-builder/agent-coordination.md +71 -0
- package/.agent/skills/app-builder/feature-building.md +53 -0
- package/.agent/skills/app-builder/project-detection.md +34 -0
- package/.agent/skills/app-builder/scaffolding.md +118 -0
- package/.agent/skills/app-builder/tech-stack.md +40 -0
- package/.agent/skills/app-builder/templates/SKILL.md +39 -0
- package/.agent/skills/app-builder/templates/astro-static/TEMPLATE.md +76 -0
- package/.agent/skills/app-builder/templates/chrome-extension/TEMPLATE.md +92 -0
- package/.agent/skills/app-builder/templates/cli-tool/TEMPLATE.md +88 -0
- package/.agent/skills/app-builder/templates/electron-desktop/TEMPLATE.md +88 -0
- package/.agent/skills/app-builder/templates/express-api/TEMPLATE.md +83 -0
- package/.agent/skills/app-builder/templates/flutter-app/TEMPLATE.md +90 -0
- package/.agent/skills/app-builder/templates/monorepo-turborepo/TEMPLATE.md +90 -0
- package/.agent/skills/app-builder/templates/nextjs-fullstack/TEMPLATE.md +122 -0
- package/.agent/skills/app-builder/templates/nextjs-saas/TEMPLATE.md +122 -0
- package/.agent/skills/app-builder/templates/nextjs-static/TEMPLATE.md +169 -0
- package/.agent/skills/app-builder/templates/nuxt-app/TEMPLATE.md +134 -0
- package/.agent/skills/app-builder/templates/python-fastapi/TEMPLATE.md +83 -0
- package/.agent/skills/app-builder/templates/react-native-app/TEMPLATE.md +119 -0
- package/.agent/skills/architecture/SKILL.md +55 -0
- package/.agent/skills/architecture/context-discovery.md +43 -0
- package/.agent/skills/architecture/examples.md +94 -0
- package/.agent/skills/architecture/pattern-selection.md +68 -0
- package/.agent/skills/architecture/patterns-reference.md +50 -0
- package/.agent/skills/architecture/trade-off-analysis.md +77 -0
- package/.agent/skills/clean-code/SKILL.md +201 -0
- package/.agent/skills/doc.md +177 -0
- package/.agent/skills/frontend-design/SKILL.md +418 -0
- package/.agent/skills/frontend-design/animation-guide.md +331 -0
- package/.agent/skills/frontend-design/color-system.md +311 -0
- package/.agent/skills/frontend-design/decision-trees.md +418 -0
- package/.agent/skills/frontend-design/motion-graphics.md +306 -0
- package/.agent/skills/frontend-design/scripts/accessibility_checker.py +183 -0
- package/.agent/skills/frontend-design/scripts/ux_audit.py +722 -0
- package/.agent/skills/frontend-design/typography-system.md +345 -0
- package/.agent/skills/frontend-design/ux-psychology.md +1116 -0
- package/.agent/skills/frontend-design/visual-effects.md +383 -0
- package/.agent/skills/i18n-localization/SKILL.md +154 -0
- package/.agent/skills/i18n-localization/scripts/i18n_checker.py +241 -0
- package/.agent/skills/mcp-builder/SKILL.md +176 -0
- package/.agent/skills/web-design-guidelines/SKILL.md +57 -0
- package/.agent/workflows/brainstorm.md +113 -0
- package/.agent/workflows/create.md +59 -0
- package/.agent/workflows/debug.md +103 -0
- package/.agent/workflows/deploy.md +176 -0
- package/.agent/workflows/enhance.md +63 -0
- package/.agent/workflows/orchestrate.md +237 -0
- package/.agent/workflows/plan.md +89 -0
- package/.agent/workflows/preview.md +81 -0
- package/.agent/workflows/simple-test.md +42 -0
- package/.agent/workflows/status.md +86 -0
- package/.agent/workflows/structured-orchestrate.md +180 -0
- package/.agent/workflows/test.md +144 -0
- package/.agent/workflows/ui-ux-pro-max.md +296 -0
- package/.claude/settings.local.json +157 -149
- package/.editorconfig +56 -0
- package/.husky/pre-commit +4 -0
- package/.lintstagedrc +7 -0
- package/.prettierignore +29 -0
- package/.prettierrc +11 -0
- package/CLAUDE.md +2 -0
- package/README.md +64 -55
- package/SPEC.md +102 -61
- package/cli/bin/foliko.js +4 -4
- package/cli/src/commands/chat.js +53 -51
- package/cli/src/commands/list.js +40 -37
- package/cli/src/index.js +18 -18
- package/cli/src/ui/chat-ui.js +78 -76
- package/cli/src/utils/ansi.js +15 -15
- package/cli/src/utils/markdown.js +112 -116
- package/docker-compose.yml +1 -1
- package/docs/ai-sdk-optimization.md +655 -636
- package/docs/features.md +80 -80
- package/docs/quick-reference.md +49 -46
- package/docs/user-manual.md +411 -380
- package/examples/ambient-example.js +95 -97
- package/examples/basic.js +115 -110
- package/examples/bootstrap.js +52 -43
- package/examples/mcp-example.js +56 -53
- package/examples/skill-example.js +49 -49
- package/examples/test-chat.js +60 -58
- package/examples/test-mcp.js +49 -43
- package/examples/test-reload.js +38 -40
- package/examples/test-telegram.js +3 -3
- package/examples/test-tg-bot.js +7 -4
- package/examples/test-tg-simple.js +4 -3
- package/examples/test-tg.js +3 -3
- package/examples/test-think.js +13 -7
- package/examples/test-web-plugin.js +61 -56
- package/examples/test-weixin-feishu.js +40 -37
- package/examples/workflow.js +49 -49
- package/foliko-1.0.75.tgz +0 -0
- package/package.json +37 -3
- package/plugins/ai-plugin.js +7 -5
- package/plugins/ambient-agent/EventWatcher.js +113 -0
- package/plugins/ambient-agent/ExplorerLoop.js +640 -0
- package/plugins/ambient-agent/GoalManager.js +197 -0
- package/plugins/ambient-agent/Reflector.js +95 -0
- package/plugins/ambient-agent/StateStore.js +90 -0
- package/plugins/ambient-agent/constants.js +101 -0
- package/plugins/ambient-agent/index.js +579 -0
- package/plugins/default-plugins.js +62 -49
- package/plugins/email/constants.js +64 -0
- package/plugins/email/handlers.js +461 -0
- package/plugins/email/index.js +278 -0
- package/plugins/email/monitor.js +269 -0
- package/plugins/email/parser.js +138 -0
- package/plugins/email/reply.js +151 -0
- package/plugins/email/utils.js +124 -0
- package/plugins/feishu-plugin.js +23 -19
- package/plugins/file-system-plugin.js +469 -120
- package/plugins/install-plugin.js +6 -4
- package/plugins/python-executor-plugin.js +3 -1
- package/plugins/python-plugin-loader.js +10 -8
- package/plugins/rules-plugin.js +5 -3
- package/plugins/scheduler-plugin.js +18 -16
- package/plugins/session-plugin.js +3 -1
- package/plugins/storage-plugin.js +5 -3
- package/plugins/subagent-plugin.js +152 -92
- package/plugins/telegram-plugin.js +26 -19
- package/plugins/think-plugin.js +4 -2
- package/plugins/tools-plugin.js +3 -1
- package/plugins/web-plugin.js +15 -13
- package/plugins/weixin-plugin.js +43 -36
- package/reports/system-health-report-20260401.md +79 -0
- package/skills/ambient-agent/SKILL.md +49 -39
- package/skills/foliko-dev/AGENTS.md +64 -61
- package/skills/foliko-dev/SKILL.md +125 -119
- package/skills/mcp-usage/SKILL.md +19 -17
- package/skills/python-plugin-dev/SKILL.md +16 -15
- package/skills/skill-guide/SKILL.md +12 -12
- package/skills/subagent-guide/SKILL.md +237 -0
- package/skills/workflow-guide/SKILL.md +90 -45
- package/skills/workflow-troubleshooting/DEBUGGING.md +36 -21
- package/skills/workflow-troubleshooting/SKILL.md +156 -79
- package/src/capabilities/index.js +4 -4
- package/src/capabilities/skill-manager.js +211 -197
- package/src/capabilities/workflow-engine.js +461 -547
- package/src/core/agent-chat.js +426 -279
- package/src/core/agent.js +453 -248
- package/src/core/framework.js +183 -149
- package/src/core/index.js +8 -8
- package/src/core/plugin-base.js +52 -52
- package/src/core/plugin-manager.js +377 -281
- package/src/core/provider.js +35 -32
- package/src/core/sub-agent-config.js +264 -0
- package/src/core/system-prompt-builder.js +120 -0
- package/src/core/tool-registry.js +416 -33
- package/src/core/tool-router.js +149 -68
- package/src/executors/executor-base.js +58 -58
- package/src/executors/mcp-executor.js +269 -257
- package/src/index.js +5 -17
- package/src/utils/circuit-breaker.js +301 -0
- package/src/utils/error-boundary.js +363 -0
- package/src/utils/error.js +374 -0
- package/src/utils/event-emitter.js +20 -20
- package/src/utils/id.js +133 -0
- package/src/utils/index.js +217 -3
- package/src/utils/logger.js +181 -0
- package/src/utils/plugin-helpers.js +90 -0
- package/src/utils/retry.js +122 -0
- package/src/utils/sandbox.js +292 -0
- package/test/tool-registry-validation.test.js +218 -0
- package/test_report.md +70 -0
- package/website/docs/api.html +169 -107
- package/website/docs/configuration.html +296 -144
- package/website/docs/plugin-development.html +154 -85
- package/website/docs/project-structure.html +110 -109
- package/website/docs/skill-development.html +117 -61
- package/website/index.html +209 -205
- package/website/script.js +20 -17
- package/website/styles.css +1 -1
- package/plugins/ambient-agent-plugin.js +0 -1565
- package/plugins/email.js +0 -1142
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Email 插件 - 自动回复
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const { EMAIL_DEFAULTS } = require('./constants')
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* 处理自动回复
|
|
9
|
+
* @param {Object} emailPlugin - EmailPlugin 实例
|
|
10
|
+
* @param {Object} args - 回复参数
|
|
11
|
+
* @returns {Promise<Object>} 回复结果
|
|
12
|
+
*/
|
|
13
|
+
async function handleAutoReply(emailPlugin, args) {
|
|
14
|
+
let { to, subject, body, from, _event, prompt, messageId, inReplyTo } = args
|
|
15
|
+
|
|
16
|
+
// 如果没有直接参数,尝试从 _event 提取
|
|
17
|
+
// 支持多种事件结构:{ email, timestamp } 或 { data: { email } } 或 { data: { ... } }
|
|
18
|
+
if (!to && !subject && !body && _event) {
|
|
19
|
+
// 兼容多种事件结构
|
|
20
|
+
const email = _event.email || _event.data?.email || _event.data || {}
|
|
21
|
+
from = email.from?.text || email.from || ''
|
|
22
|
+
to = email.to?.text || email.to || ''
|
|
23
|
+
subject = email.subject || ''
|
|
24
|
+
body = email.text || email.body || ''
|
|
25
|
+
// 优先使用 _event 中的 uid/messageId
|
|
26
|
+
if (!messageId) {
|
|
27
|
+
messageId = email.messageId || email.uid || _event.data?.messageId || _event.data?.uid
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// 如果 _event 没有有效数据,不允许自动读取所有邮件
|
|
32
|
+
if (!_event || (!_event.email && !_event.data)) {
|
|
33
|
+
return {
|
|
34
|
+
success: false,
|
|
35
|
+
error: '缺少邮件数据:_event 中没有有效的邮件信息,无法自动回复。请确认是否在收到新邮件事件时调用此工具。'
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// 优先使用传入的 inReplyTo,否则用 messageId
|
|
40
|
+
const replyTo = inReplyTo || messageId
|
|
41
|
+
|
|
42
|
+
// 检查必要参数
|
|
43
|
+
if (!from && !to) {
|
|
44
|
+
return { success: false, error: '缺少收件人地址' }
|
|
45
|
+
}
|
|
46
|
+
if (!body) {
|
|
47
|
+
return { success: false, error: '缺少邮件内容' }
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
try {
|
|
51
|
+
// 使用子 Agent 生成回复
|
|
52
|
+
const replyAgent = emailPlugin._framework.createSubAgent({
|
|
53
|
+
name: 'email_replier',
|
|
54
|
+
role: '邮件自动回复助手,专注于生成专业、礼貌、简洁的邮件回复',
|
|
55
|
+
parentTools: []
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
// 构建提示让 LLM 生成回复
|
|
59
|
+
const finalPrompt = prompt || `你是一封邮件自动回复助手。请根据以下邮件内容,生成一封专业的回复邮件。
|
|
60
|
+
|
|
61
|
+
【原始邮件】
|
|
62
|
+
发件人: ${from || to}
|
|
63
|
+
主题: ${subject}
|
|
64
|
+
内容:
|
|
65
|
+
${body}
|
|
66
|
+
|
|
67
|
+
【要求】
|
|
68
|
+
1. 回复内容要针对邮件中的问题或内容进行回复
|
|
69
|
+
2. 语言要专业、礼貌、简洁
|
|
70
|
+
3. 只输出邮件正文内容,不要额外解释
|
|
71
|
+
4. 回复语言应与原邮件一致(如果原邮件是中文,则用中文回复)`
|
|
72
|
+
|
|
73
|
+
// 等待 Agent 生成回复(带超时保护)
|
|
74
|
+
const timeoutMs = args.timeout || EMAIL_DEFAULTS.timeout
|
|
75
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
76
|
+
setTimeout(() => reject(new Error(`AI回复生成超时(${timeoutMs / 1000}秒)`)), timeoutMs)
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
const replyPromise = replyAgent.chat(finalPrompt, { maxSteps: 3 })
|
|
80
|
+
const replyResult = await Promise.race([replyPromise, timeoutPromise])
|
|
81
|
+
|
|
82
|
+
// 提取回复内容
|
|
83
|
+
let replyContent = ''
|
|
84
|
+
if (typeof replyResult === 'string') {
|
|
85
|
+
replyContent = replyResult.trim()
|
|
86
|
+
} else if (replyResult && replyResult.content) {
|
|
87
|
+
replyContent = replyResult.content.trim()
|
|
88
|
+
} else if (replyResult && replyResult.message) {
|
|
89
|
+
replyContent = replyResult.message.trim()
|
|
90
|
+
} else {
|
|
91
|
+
replyContent = JSON.stringify(replyResult).trim()
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// 去掉思考过程标签
|
|
95
|
+
replyContent = replyContent.replace(/<think>[\s\S]*?<\/think>/g, '').trim()
|
|
96
|
+
|
|
97
|
+
if (!replyContent || replyContent.length < 5) {
|
|
98
|
+
return { success: false, error: 'AI未能生成有效的回复内容' }
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// 发送回复邮件
|
|
102
|
+
const { sendEmail } = require('./handlers')
|
|
103
|
+
const sendResult = await sendEmail(emailPlugin, {
|
|
104
|
+
to: from || to,
|
|
105
|
+
subject: `Re: ${subject}`,
|
|
106
|
+
body: replyContent,
|
|
107
|
+
inReplyTo: replyTo,
|
|
108
|
+
references: replyTo ? [replyTo] : undefined
|
|
109
|
+
})
|
|
110
|
+
|
|
111
|
+
if (sendResult.success) {
|
|
112
|
+
// 发送成功后,标记为已处理
|
|
113
|
+
const emailData = _event?.email || _event?.data?.email || _event?.data || {}
|
|
114
|
+
const msgId = messageId || emailData.messageId || emailData.uid
|
|
115
|
+
if (msgId) {
|
|
116
|
+
emailPlugin._processedEmails.add(msgId)
|
|
117
|
+
emailPlugin._log.info(`邮件已处理: ${msgId}`)
|
|
118
|
+
|
|
119
|
+
// 24小时后从已处理列表中移除
|
|
120
|
+
setTimeout(() => {
|
|
121
|
+
emailPlugin._processedEmails.delete(msgId)
|
|
122
|
+
}, EMAIL_DEFAULTS.processedEmailTTL)
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// 自动标记原邮件为已读
|
|
126
|
+
const uid = emailData.uid || msgId
|
|
127
|
+
if (uid) {
|
|
128
|
+
try {
|
|
129
|
+
const { markAsRead } = require('./handlers')
|
|
130
|
+
await markAsRead(emailPlugin, { uid: uid }) // 使用 uid 而非 messageId
|
|
131
|
+
} catch (err) {
|
|
132
|
+
emailPlugin._log.warn(`自动标记已读失败: ${err.message}`)
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return {
|
|
137
|
+
success: true,
|
|
138
|
+
message: `自动回复已发送至 ${from || to}`,
|
|
139
|
+
replyContent
|
|
140
|
+
}
|
|
141
|
+
} else {
|
|
142
|
+
return { success: false, error: sendResult.error || '发送失败' }
|
|
143
|
+
}
|
|
144
|
+
} catch (err) {
|
|
145
|
+
return { success: false, error: err.message }
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
module.exports = {
|
|
150
|
+
handleAutoReply
|
|
151
|
+
}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Email 插件 - 工具函数
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const https = require('https')
|
|
6
|
+
const http = require('http')
|
|
7
|
+
const fs = require('fs')
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* 验证邮箱地址格式
|
|
11
|
+
* @param {string} email - 邮箱地址
|
|
12
|
+
* @returns {boolean}
|
|
13
|
+
*/
|
|
14
|
+
function isValidEmail(email) {
|
|
15
|
+
if (!email) return false
|
|
16
|
+
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
|
|
17
|
+
return emailRegex.test(email)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* 从邮件地址提取显示名称和邮箱
|
|
22
|
+
* @param {string} from - 邮件地址文本
|
|
23
|
+
* @returns {{name: string, email: string}}
|
|
24
|
+
*/
|
|
25
|
+
function parseEmailAddress(from) {
|
|
26
|
+
if (!from) return { name: '', email: '' }
|
|
27
|
+
|
|
28
|
+
// 格式: "Display Name <email@example.com>"
|
|
29
|
+
const match = from.match(/^(.+?)\s*<(.+)>$/)
|
|
30
|
+
if (match) {
|
|
31
|
+
return { name: match[1].trim(), email: match[2].trim() }
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// 格式: email@example.com
|
|
35
|
+
return { name: '', email: from.trim() }
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* 获取环境变量配置
|
|
40
|
+
* @param {Object} args - 参数对象
|
|
41
|
+
* @returns {Object} 合并后的配置
|
|
42
|
+
*/
|
|
43
|
+
function getConfig(args) {
|
|
44
|
+
return {
|
|
45
|
+
host: args.host || process.env.IMAP_HOST,
|
|
46
|
+
port: args.port || parseInt(process.env.IMAP_PORT) || 993,
|
|
47
|
+
user: args.user || process.env.IMAP_USER,
|
|
48
|
+
password: args.password || process.env.IMAP_PASS
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* 从 URL 获取内容
|
|
54
|
+
* @param {string} url - 文件 URL
|
|
55
|
+
* @returns {Promise<Buffer>}
|
|
56
|
+
*/
|
|
57
|
+
function fetchUrl(url) {
|
|
58
|
+
return new Promise((resolve, reject) => {
|
|
59
|
+
const lib = url.startsWith('https') ? https : http
|
|
60
|
+
|
|
61
|
+
const request = lib.get(url, (response) => {
|
|
62
|
+
// 处理重定向
|
|
63
|
+
if (response.statusCode === 301 || response.statusCode === 302) {
|
|
64
|
+
fetchUrl(response.headers.location).then(resolve).catch(reject)
|
|
65
|
+
return
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const chunks = []
|
|
69
|
+
response.on('data', (chunk) => chunks.push(chunk))
|
|
70
|
+
response.on('end', () => resolve(Buffer.concat(chunks)))
|
|
71
|
+
response.on('error', reject)
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
request.on('error', reject)
|
|
75
|
+
request.setTimeout(10000, () => {
|
|
76
|
+
request.destroy()
|
|
77
|
+
reject(new Error('下载超时'))
|
|
78
|
+
})
|
|
79
|
+
})
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* 处理附件
|
|
84
|
+
* @param {Object} att - 附件定义
|
|
85
|
+
* @returns {Promise<Object>} 处理后的附件
|
|
86
|
+
*/
|
|
87
|
+
async function processAttachment(att) {
|
|
88
|
+
const attachment = { filename: att.filename }
|
|
89
|
+
|
|
90
|
+
if (att.path) {
|
|
91
|
+
// 本地文件
|
|
92
|
+
attachment.content = fs.createReadStream(att.path)
|
|
93
|
+
} else if (att.url) {
|
|
94
|
+
// 远程 URL
|
|
95
|
+
attachment.content = await fetchUrl(att.url)
|
|
96
|
+
} else if (att.content) {
|
|
97
|
+
// Base64 内容
|
|
98
|
+
const base64Data = att.content.replace(/^data:[^;]+;base64,/, '')
|
|
99
|
+
attachment.content = Buffer.from(base64Data, 'base64')
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (att.cid) {
|
|
103
|
+
attachment.cid = att.cid
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return attachment
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* 生成唯一调用 ID
|
|
111
|
+
* @returns {string}
|
|
112
|
+
*/
|
|
113
|
+
function generateCallId() {
|
|
114
|
+
return `${Date.now()}_${Math.random().toString(36).substr(2, 5)}`
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
module.exports = {
|
|
118
|
+
isValidEmail,
|
|
119
|
+
parseEmailAddress,
|
|
120
|
+
getConfig,
|
|
121
|
+
fetchUrl,
|
|
122
|
+
processAttachment,
|
|
123
|
+
generateCallId
|
|
124
|
+
}
|
package/plugins/feishu-plugin.js
CHANGED
|
@@ -9,6 +9,8 @@
|
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
const { Plugin } = require('../src/core/plugin-base')
|
|
12
|
+
const { logger } = require('../src/utils/logger')
|
|
13
|
+
const log = logger.child('Feishu')
|
|
12
14
|
const {cleanResponse} =require('../src/utils')
|
|
13
15
|
const removeMarkdown = require('remove-markdown');
|
|
14
16
|
class FeishuPlugin extends Plugin {
|
|
@@ -19,10 +21,12 @@ class FeishuPlugin extends Plugin {
|
|
|
19
21
|
this.description = '飞书对话插件,使用 WebSocket 长连接接收消息'
|
|
20
22
|
this.priority = 80
|
|
21
23
|
this.enabled = false
|
|
22
|
-
this.systemPrompt =
|
|
24
|
+
this.systemPrompt = `你是一个飞书助手。回复内容不要使用markdown格式文本。
|
|
23
25
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
+
**重要:** 子Agent 匹配规则必须遵守:
|
|
27
|
+
- 根据【子 Agent 匹配表】,将任务委托给最匹配的子Agent处理
|
|
28
|
+
- 使用 subagent_call 工具并指定 agentName 来委托任务
|
|
29
|
+
- 只有当没有匹配的子Agent时,才直接调用工具
|
|
26
30
|
|
|
27
31
|
**命令执行规范:**
|
|
28
32
|
- 执行 npx skills add / npx skills remove / npx skills list 命令时,必须自动添加参数:-a openclaw -y
|
|
@@ -57,7 +61,7 @@ class FeishuPlugin extends Plugin {
|
|
|
57
61
|
this._initialized = true
|
|
58
62
|
|
|
59
63
|
if (!this.config.appId || !this.config.appSecret) {
|
|
60
|
-
|
|
64
|
+
log.warn(' No appId or appSecret.')
|
|
61
65
|
return this
|
|
62
66
|
}
|
|
63
67
|
|
|
@@ -67,7 +71,7 @@ class FeishuPlugin extends Plugin {
|
|
|
67
71
|
if (this._sessionPlugin) {
|
|
68
72
|
this._sessionDeleteHandler = (sessionId) => {
|
|
69
73
|
if (sessionId.startsWith('feishu_')) {
|
|
70
|
-
|
|
74
|
+
log.info(` Session deleted: ${sessionId}`)
|
|
71
75
|
}
|
|
72
76
|
}
|
|
73
77
|
this._sessionPlugin.on('session:deleted', this._sessionDeleteHandler)
|
|
@@ -121,9 +125,9 @@ class FeishuPlugin extends Plugin {
|
|
|
121
125
|
})
|
|
122
126
|
}
|
|
123
127
|
|
|
124
|
-
|
|
128
|
+
log.info(' WebSocket client started')
|
|
125
129
|
} catch (err) {
|
|
126
|
-
|
|
130
|
+
log.error(' Failed to initialize client:', err.message, err.stack)
|
|
127
131
|
}
|
|
128
132
|
}
|
|
129
133
|
|
|
@@ -172,7 +176,7 @@ class FeishuPlugin extends Plugin {
|
|
|
172
176
|
this._processingLock.set(openId, false)
|
|
173
177
|
}
|
|
174
178
|
} catch (err) {
|
|
175
|
-
|
|
179
|
+
log.error(' Error handling message:', err.message)
|
|
176
180
|
}
|
|
177
181
|
}
|
|
178
182
|
|
|
@@ -238,7 +242,7 @@ class FeishuPlugin extends Plugin {
|
|
|
238
242
|
try {
|
|
239
243
|
await this._sendTextMessage(openId, reminderText)
|
|
240
244
|
} catch (err) {
|
|
241
|
-
|
|
245
|
+
log.error(` Failed to send reminder:`, err.message)
|
|
242
246
|
}
|
|
243
247
|
return
|
|
244
248
|
}
|
|
@@ -253,7 +257,7 @@ class FeishuPlugin extends Plugin {
|
|
|
253
257
|
try {
|
|
254
258
|
await this._sendTextMessage(openId, reminderText)
|
|
255
259
|
} catch (err) {
|
|
256
|
-
|
|
260
|
+
log.error(` Failed to send reminder:`, err.message)
|
|
257
261
|
}
|
|
258
262
|
}
|
|
259
263
|
}
|
|
@@ -274,9 +278,9 @@ class FeishuPlugin extends Plugin {
|
|
|
274
278
|
|
|
275
279
|
try {
|
|
276
280
|
await this._sendTextMessage(openId, notificationText)
|
|
277
|
-
|
|
281
|
+
log.info(` Webhook notification sent to ${openId}`)
|
|
278
282
|
} catch (err) {
|
|
279
|
-
|
|
283
|
+
log.error(` Failed to send webhook notification:`, err.message)
|
|
280
284
|
}
|
|
281
285
|
}
|
|
282
286
|
|
|
@@ -311,7 +315,7 @@ class FeishuPlugin extends Plugin {
|
|
|
311
315
|
}
|
|
312
316
|
|
|
313
317
|
if (!openId) {
|
|
314
|
-
|
|
318
|
+
log.warn(' No feishu session found for notification')
|
|
315
319
|
return
|
|
316
320
|
}
|
|
317
321
|
|
|
@@ -327,9 +331,9 @@ class FeishuPlugin extends Plugin {
|
|
|
327
331
|
|
|
328
332
|
try {
|
|
329
333
|
await this._sendTextMessage(openId, notificationText)
|
|
330
|
-
|
|
334
|
+
log.info(` Notification sent to ${openId}`)
|
|
331
335
|
} catch (err) {
|
|
332
|
-
|
|
336
|
+
log.error(` Failed to send notification:`, err.message)
|
|
333
337
|
}
|
|
334
338
|
}
|
|
335
339
|
|
|
@@ -358,7 +362,7 @@ class FeishuPlugin extends Plugin {
|
|
|
358
362
|
await this._sendMessage(openId, '抱歉,我没有收到有效的回复。', originalMsg)
|
|
359
363
|
}
|
|
360
364
|
} catch (err) {
|
|
361
|
-
|
|
365
|
+
log.error(' Chat error:', err)
|
|
362
366
|
await this._sendMessage(openId, `发生错误:${err.message}`, originalMsg)
|
|
363
367
|
}
|
|
364
368
|
}
|
|
@@ -375,7 +379,7 @@ class FeishuPlugin extends Plugin {
|
|
|
375
379
|
}
|
|
376
380
|
})
|
|
377
381
|
} catch (err) {
|
|
378
|
-
|
|
382
|
+
log.error(' Failed to send text message:', err.message)
|
|
379
383
|
throw err
|
|
380
384
|
}
|
|
381
385
|
}
|
|
@@ -396,7 +400,7 @@ class FeishuPlugin extends Plugin {
|
|
|
396
400
|
}
|
|
397
401
|
})
|
|
398
402
|
} catch (err) {
|
|
399
|
-
|
|
403
|
+
log.error(' Failed to send message:', err.message)
|
|
400
404
|
}
|
|
401
405
|
}
|
|
402
406
|
|
|
@@ -437,7 +441,7 @@ class FeishuPlugin extends Plugin {
|
|
|
437
441
|
try { this._wsClient.close() } catch (e) { /* ignore */ }
|
|
438
442
|
this._wsClient = null
|
|
439
443
|
this._client = null
|
|
440
|
-
|
|
444
|
+
log.info(' Client stopped')
|
|
441
445
|
}
|
|
442
446
|
}
|
|
443
447
|
|