foliko 1.0.85 → 1.0.86
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/agents/code-assistant.json +14 -0
- package/.agent/agents/email-assistant.json +14 -0
- package/.agent/agents/file-assistant.json +15 -0
- package/.agent/agents/system-assistant.json +15 -0
- package/.agent/agents/web-assistant.json +12 -0
- package/.agent/data/ambient/goals.json +50 -0
- package/.agent/data/ambient/memories.json +7 -0
- package/.agent/data/default.json +4156 -244
- package/.agent/data/plugins-state.json +162 -174
- package/.agent/data/scheduler/tasks.json +1 -0
- package/.agent/data/weixin.json +6 -0
- package/.agent/mcp_config.json +1 -0
- package/.agent/package.json +8 -0
- package/.agent/plugins/__pycache__/test_plugin.cpython-312.pyc +0 -0
- package/.agent/plugins/daytona/README.md +89 -0
- package/.agent/plugins/daytona/index.js +377 -0
- package/.agent/plugins/daytona/package.json +12 -0
- package/.agent/plugins/marknative/README.md +134 -0
- package/.agent/plugins/marknative/index.js +233 -0
- package/.agent/plugins/marknative/package.json +12 -0
- package/.agent/plugins/marknative/update-readme.js +134 -0
- package/.agent/plugins/system-info/index.js +387 -0
- package/.agent/plugins/system-info/package.json +4 -0
- package/.agent/plugins/system-info/test.js +40 -0
- package/.agent/plugins/temp-repo/LICENSE +201 -0
- package/.agent/plugins/test_plugin.py +304 -0
- package/.agent/plugins.json +14 -5
- package/.agent/python-scripts/test_sample.py +24 -0
- package/.agent/skills/agent-browser/SKILL.md +311 -0
- package/.agent/skills/agent-browser/TEST_PLAN.md +200 -0
- package/.agent/skills/sysinfo/SKILL.md +38 -0
- package/.agent/skills/sysinfo/system-info.sh +130 -0
- package/.agent/skills/workflow/SKILL.md +324 -0
- package/.agent/workflows/email-digest.json +50 -0
- package/.agent/workflows/file-backup.json +21 -0
- package/.agent/workflows/get-ip-notify.json +32 -0
- package/.agent/workflows/news-aggregator.json +93 -0
- package/.agent/workflows/news-dashboard-v2.json +94 -0
- package/.agent/workflows/notification-batch.json +32 -0
- package/.claude/settings.local.json +171 -178
- package/.env.example +56 -56
- package/README.md +441 -441
- package/package.json +2 -2
- package/plugins/ambient-agent/EventWatcher.js +4 -4
- package/plugins/extension-executor-plugin.js +44 -1
- package/plugins/file-system-plugin.js +44 -5
- package/plugins/weixin-plugin.js +278 -29
- package/skills/find-skills/AGENTS.md +162 -162
- package/skills/find-skills/SKILL.md +133 -133
- package/skills/foliko-dev/SKILL.md +67 -0
- package/skills/python-plugin-dev/SKILL.md +238 -238
- package/src/core/agent-chat.js +4 -3
- package/.agent/.shared/ui-ux-pro-max/data/charts.csv +0 -26
- package/.agent/.shared/ui-ux-pro-max/data/colors.csv +0 -97
- package/.agent/.shared/ui-ux-pro-max/data/icons.csv +0 -101
- package/.agent/.shared/ui-ux-pro-max/data/landing.csv +0 -31
- package/.agent/.shared/ui-ux-pro-max/data/products.csv +0 -97
- package/.agent/.shared/ui-ux-pro-max/data/prompts.csv +0 -24
- package/.agent/.shared/ui-ux-pro-max/data/react-performance.csv +0 -45
- package/.agent/.shared/ui-ux-pro-max/data/stacks/flutter.csv +0 -53
- package/.agent/.shared/ui-ux-pro-max/data/stacks/html-tailwind.csv +0 -56
- package/.agent/.shared/ui-ux-pro-max/data/stacks/jetpack-compose.csv +0 -53
- package/.agent/.shared/ui-ux-pro-max/data/stacks/nextjs.csv +0 -53
- package/.agent/.shared/ui-ux-pro-max/data/stacks/nuxt-ui.csv +0 -51
- package/.agent/.shared/ui-ux-pro-max/data/stacks/nuxtjs.csv +0 -59
- package/.agent/.shared/ui-ux-pro-max/data/stacks/react-native.csv +0 -52
- package/.agent/.shared/ui-ux-pro-max/data/stacks/react.csv +0 -54
- package/.agent/.shared/ui-ux-pro-max/data/stacks/shadcn.csv +0 -61
- package/.agent/.shared/ui-ux-pro-max/data/stacks/svelte.csv +0 -54
- package/.agent/.shared/ui-ux-pro-max/data/stacks/swiftui.csv +0 -51
- package/.agent/.shared/ui-ux-pro-max/data/stacks/vue.csv +0 -50
- package/.agent/.shared/ui-ux-pro-max/data/styles.csv +0 -59
- package/.agent/.shared/ui-ux-pro-max/data/typography.csv +0 -58
- package/.agent/.shared/ui-ux-pro-max/data/ui-reasoning.csv +0 -101
- package/.agent/.shared/ui-ux-pro-max/data/ux-guidelines.csv +0 -100
- package/.agent/.shared/ui-ux-pro-max/data/web-interface.csv +0 -31
- 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 +0 -258
- package/.agent/.shared/ui-ux-pro-max/scripts/design_system.py +0 -1067
- package/.agent/.shared/ui-ux-pro-max/scripts/search.py +0 -106
- package/.agent/ARCHITECTURE.md +0 -288
- package/.agent/agents/ambient-agent.md +0 -57
- package/.agent/agents/debugger.md +0 -55
- package/.agent/agents/email-assistant.md +0 -49
- package/.agent/agents/file-manager.md +0 -42
- package/.agent/agents/python-developer.md +0 -60
- package/.agent/agents/scheduler.md +0 -59
- package/.agent/agents/web-developer.md +0 -45
- package/.agent/data/puppeteer-sessions/undefined.json +0 -6
- package/.agent/mcp_config_updated.json +0 -12
- package/.agent/rules/GEMINI.md +0 -273
- package/.agent/rules/allow-rule.md +0 -77
- package/.agent/rules/log-rule.md +0 -83
- package/.agent/rules/security-rule.md +0 -93
- package/.agent/scripts/auto_preview.py +0 -148
- package/.agent/scripts/checklist.py +0 -217
- package/.agent/scripts/session_manager.py +0 -120
- package/.agent/scripts/verify_all.py +0 -327
- package/.agent/skills/api-patterns/SKILL.md +0 -81
- package/.agent/skills/api-patterns/api-style.md +0 -42
- package/.agent/skills/api-patterns/auth.md +0 -24
- package/.agent/skills/api-patterns/documentation.md +0 -26
- package/.agent/skills/api-patterns/graphql.md +0 -41
- package/.agent/skills/api-patterns/rate-limiting.md +0 -31
- package/.agent/skills/api-patterns/response.md +0 -37
- package/.agent/skills/api-patterns/rest.md +0 -40
- package/.agent/skills/api-patterns/scripts/api_validator.py +0 -211
- package/.agent/skills/api-patterns/security-testing.md +0 -122
- package/.agent/skills/api-patterns/trpc.md +0 -41
- package/.agent/skills/api-patterns/versioning.md +0 -22
- package/.agent/skills/app-builder/SKILL.md +0 -75
- package/.agent/skills/app-builder/agent-coordination.md +0 -71
- package/.agent/skills/app-builder/feature-building.md +0 -53
- package/.agent/skills/app-builder/project-detection.md +0 -34
- package/.agent/skills/app-builder/scaffolding.md +0 -118
- package/.agent/skills/app-builder/tech-stack.md +0 -40
- package/.agent/skills/app-builder/templates/SKILL.md +0 -39
- package/.agent/skills/app-builder/templates/astro-static/TEMPLATE.md +0 -76
- package/.agent/skills/app-builder/templates/chrome-extension/TEMPLATE.md +0 -92
- package/.agent/skills/app-builder/templates/cli-tool/TEMPLATE.md +0 -88
- package/.agent/skills/app-builder/templates/electron-desktop/TEMPLATE.md +0 -88
- package/.agent/skills/app-builder/templates/express-api/TEMPLATE.md +0 -83
- package/.agent/skills/app-builder/templates/flutter-app/TEMPLATE.md +0 -90
- package/.agent/skills/app-builder/templates/monorepo-turborepo/TEMPLATE.md +0 -90
- package/.agent/skills/app-builder/templates/nextjs-fullstack/TEMPLATE.md +0 -122
- package/.agent/skills/app-builder/templates/nextjs-saas/TEMPLATE.md +0 -122
- package/.agent/skills/app-builder/templates/nextjs-static/TEMPLATE.md +0 -169
- package/.agent/skills/app-builder/templates/nuxt-app/TEMPLATE.md +0 -134
- package/.agent/skills/app-builder/templates/python-fastapi/TEMPLATE.md +0 -83
- package/.agent/skills/app-builder/templates/react-native-app/TEMPLATE.md +0 -119
- package/.agent/skills/architecture/SKILL.md +0 -55
- package/.agent/skills/architecture/context-discovery.md +0 -43
- package/.agent/skills/architecture/examples.md +0 -94
- package/.agent/skills/architecture/pattern-selection.md +0 -68
- package/.agent/skills/architecture/patterns-reference.md +0 -50
- package/.agent/skills/architecture/trade-off-analysis.md +0 -77
- package/.agent/skills/clean-code/SKILL.md +0 -201
- package/.agent/skills/doc.md +0 -177
- package/.agent/skills/frontend-design/SKILL.md +0 -418
- package/.agent/skills/frontend-design/animation-guide.md +0 -331
- package/.agent/skills/frontend-design/color-system.md +0 -311
- package/.agent/skills/frontend-design/decision-trees.md +0 -418
- package/.agent/skills/frontend-design/motion-graphics.md +0 -306
- package/.agent/skills/frontend-design/scripts/accessibility_checker.py +0 -183
- package/.agent/skills/frontend-design/scripts/ux_audit.py +0 -722
- package/.agent/skills/frontend-design/typography-system.md +0 -345
- package/.agent/skills/frontend-design/ux-psychology.md +0 -1116
- package/.agent/skills/frontend-design/visual-effects.md +0 -383
- package/.agent/skills/i18n-localization/SKILL.md +0 -154
- package/.agent/skills/i18n-localization/scripts/i18n_checker.py +0 -241
- package/.agent/skills/mcp-builder/SKILL.md +0 -176
- package/.agent/skills/web-design-guidelines/SKILL.md +0 -57
- package/.agent/workflows/brainstorm.md +0 -113
- package/.agent/workflows/create.md +0 -59
- package/.agent/workflows/debug.md +0 -103
- package/.agent/workflows/deploy.md +0 -176
- package/.agent/workflows/enhance.md +0 -63
- package/.agent/workflows/orchestrate.md +0 -237
- package/.agent/workflows/plan.md +0 -89
- package/.agent/workflows/preview.md +0 -81
- package/.agent/workflows/simple-test.md +0 -42
- package/.agent/workflows/status.md +0 -86
- package/.agent/workflows/structured-orchestrate.md +0 -180
- package/.agent/workflows/test.md +0 -144
- package/.agent/workflows/ui-ux-pro-max.md +0 -296
- /package/.agent/plugins/{puppeteer-plugin → temp-repo/puppeteer-plugin}/README.md +0 -0
- /package/.agent/plugins/{puppeteer-plugin → temp-repo/puppeteer-plugin}/index.js +0 -0
- /package/.agent/plugins/{puppeteer-plugin → temp-repo/puppeteer-plugin}/package.json +0 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "foliko",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.86",
|
|
4
4
|
"description": "简约的插件化 Agent 框架",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"type": "commonjs",
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
"@ai-sdk/openai": "^3.0.41",
|
|
54
54
|
"@ai-sdk/openai-compatible": "^2.0.35",
|
|
55
55
|
"@anthropic-ai/sdk": "^0.39.0",
|
|
56
|
-
"@chnak/weixin-bot": "^1.2.
|
|
56
|
+
"@chnak/weixin-bot": "^1.2.7",
|
|
57
57
|
"@chnak/zod-to-markdown": "^1.0.1",
|
|
58
58
|
"@hono/node-server": "^1.19.11",
|
|
59
59
|
"@larksuiteoapi/node-sdk": "^1.59.0",
|
|
@@ -53,17 +53,17 @@ class EventWatcher {
|
|
|
53
53
|
*/
|
|
54
54
|
_handleEvent(type, data) {
|
|
55
55
|
const activeGoals = this._goalManager.getActiveGoals()
|
|
56
|
-
log.info(`_handleEvent: type=${type}, activeGoals=${activeGoals.length}`)
|
|
56
|
+
// log.info(`_handleEvent: type=${type}, activeGoals=${activeGoals.length}`)
|
|
57
57
|
|
|
58
58
|
for (const goal of activeGoals) {
|
|
59
59
|
// 检查目标条件是否匹配此事件
|
|
60
60
|
const isRelevant = this._isRelevantToGoal(goal, type, data)
|
|
61
|
-
log.info(`goal=${goal.id}, isRelevant=${isRelevant}, conditions=${JSON.stringify(goal.conditions)}`)
|
|
61
|
+
// log.info(`goal=${goal.id}, isRelevant=${isRelevant}, conditions=${JSON.stringify(goal.conditions)}`)
|
|
62
62
|
|
|
63
63
|
if (isRelevant) {
|
|
64
64
|
// 将 data 展平到事件对象中,方便通过 {{_event.xxx}} 访问
|
|
65
65
|
this._goalManager.addEventToGoal(goal.id, { type, ...data })
|
|
66
|
-
log.info(`Event added to goal ${goal.id}`)
|
|
66
|
+
// log.info(`Event added to goal ${goal.id}`)
|
|
67
67
|
}
|
|
68
68
|
}
|
|
69
69
|
|
|
@@ -75,7 +75,7 @@ class EventWatcher {
|
|
|
75
75
|
this._goalManager.activateGoal(goal.id)
|
|
76
76
|
// 添加事件
|
|
77
77
|
this._goalManager.addEventToGoal(goal.id, { type, ...data })
|
|
78
|
-
log.info(`事件触发激活目标: ${goal.id} (事件: ${type})`)
|
|
78
|
+
// log.info(`事件触发激活目标: ${goal.id} (事件: ${type})`)
|
|
79
79
|
}
|
|
80
80
|
}
|
|
81
81
|
}
|
|
@@ -31,6 +31,11 @@ class ExtensionExecutorPlugin extends Plugin {
|
|
|
31
31
|
this._scanPluginTools(plugin);
|
|
32
32
|
});
|
|
33
33
|
|
|
34
|
+
// 监听插件重载事件,重新扫描工具
|
|
35
|
+
framework.on('plugin:reloaded', (plugin) => {
|
|
36
|
+
this._rescanPluginTools(plugin);
|
|
37
|
+
});
|
|
38
|
+
|
|
34
39
|
return this;
|
|
35
40
|
}
|
|
36
41
|
|
|
@@ -72,6 +77,34 @@ class ExtensionExecutorPlugin extends Plugin {
|
|
|
72
77
|
}
|
|
73
78
|
}
|
|
74
79
|
|
|
80
|
+
/**
|
|
81
|
+
* 重新扫描插件的工具(用于热重载)
|
|
82
|
+
*/
|
|
83
|
+
_rescanPluginTools(plugin) {
|
|
84
|
+
if (!plugin || !plugin.tools || typeof plugin.tools !== 'object') {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const pluginName = plugin.name;
|
|
89
|
+
if (!pluginName) return;
|
|
90
|
+
|
|
91
|
+
// 先移除旧工具
|
|
92
|
+
if (this._extensions.has(pluginName)) {
|
|
93
|
+
const ext = this._extensions.get(pluginName);
|
|
94
|
+
for (const tool of ext.tools || []) {
|
|
95
|
+
// 从 toolRouter 移除
|
|
96
|
+
if (this._framework?.toolRouter) {
|
|
97
|
+
this._framework.toolRouter.unregister(`ext_${tool.name}`);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
this._extensions.delete(pluginName);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// 重新扫描工具
|
|
104
|
+
this._scanPluginTools(plugin);
|
|
105
|
+
log.info(` Rescanned tools from plugin '${pluginName}'`);
|
|
106
|
+
}
|
|
107
|
+
|
|
75
108
|
async start(framework) {
|
|
76
109
|
// 扫描所有已加载插件的 tools
|
|
77
110
|
const plugins = framework.pluginManager.getAll();
|
|
@@ -217,7 +250,17 @@ class ExtensionExecutorPlugin extends Plugin {
|
|
|
217
250
|
}
|
|
218
251
|
|
|
219
252
|
const ext = this._extensions.get(pluginName);
|
|
220
|
-
|
|
253
|
+
const existingIdx = ext.tools.findIndex((t) => t.name === toolDef.name);
|
|
254
|
+
if (existingIdx >= 0) {
|
|
255
|
+
// 更新已存在的工具
|
|
256
|
+
ext.tools[existingIdx] = {
|
|
257
|
+
name: toolDef.name,
|
|
258
|
+
description: toolDef.description || '',
|
|
259
|
+
inputSchema: toolDef.inputSchema,
|
|
260
|
+
execute: toolDef.execute,
|
|
261
|
+
};
|
|
262
|
+
} else {
|
|
263
|
+
// 添加新工具
|
|
221
264
|
ext.tools.push({
|
|
222
265
|
name: toolDef.name,
|
|
223
266
|
description: toolDef.description || '',
|
|
@@ -154,11 +154,10 @@ class FileSystemPlugin extends Plugin {
|
|
|
154
154
|
// 写入文件
|
|
155
155
|
framework.registerTool({
|
|
156
156
|
name: 'write_file',
|
|
157
|
-
description:
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
- 支持覆盖写入和追加写入`,
|
|
157
|
+
description: `创建或写入文件内容(仅适用于小文件,建议小于10KB)。
|
|
158
|
+
|
|
159
|
+
重要:大文件必须使用 append_to_file 分多次写入,每次追加一部分内容。
|
|
160
|
+
禁止将大段内容作为 content 参数传入,这会导致工具调用失败。`,
|
|
162
161
|
inputSchema: z.object({
|
|
163
162
|
path: z.string().describe('文件路径'),
|
|
164
163
|
content: z.string().describe('文件内容'),
|
|
@@ -225,6 +224,46 @@ class FileSystemPlugin extends Plugin {
|
|
|
225
224
|
}
|
|
226
225
|
})
|
|
227
226
|
|
|
227
|
+
// 追加文件内容(用于大文件分段写入)
|
|
228
|
+
framework.registerTool({
|
|
229
|
+
name: 'append_to_file',
|
|
230
|
+
description: `追加内容到文件末尾。适用于大文件写入,每次最多追加约64KB内容。
|
|
231
|
+
|
|
232
|
+
使用方式:
|
|
233
|
+
1. 先用 write_file 创建新文件(或清空现有文件)
|
|
234
|
+
2. 多次调用 append_to_file 追加内容,每次 content 不超过 64KB
|
|
235
|
+
3. 追加顺序很重要,content 应该按文件顺序排列`,
|
|
236
|
+
inputSchema: z.object({
|
|
237
|
+
path: z.string().describe('文件路径'),
|
|
238
|
+
content: z.string().describe('要追加的内容(建议不超过64KB)')
|
|
239
|
+
}),
|
|
240
|
+
execute: async (args, framework) => {
|
|
241
|
+
const { path: filePath, content } = args
|
|
242
|
+
|
|
243
|
+
const pathCheck = validatePath(filePath)
|
|
244
|
+
if (!pathCheck.valid) {
|
|
245
|
+
return { success: false, error: pathCheck.error }
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
try {
|
|
249
|
+
// 如果文件不存在,返回错误(应该先用 write_file 创建)
|
|
250
|
+
if (!fs.existsSync(pathCheck.resolved)) {
|
|
251
|
+
return { success: false, error: '文件不存在,请先用 write_file 创建文件' }
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
fs.appendFileSync(pathCheck.resolved, content, 'utf8')
|
|
255
|
+
return {
|
|
256
|
+
success: true,
|
|
257
|
+
message: `已追加内容到: ${pathCheck.resolved}`,
|
|
258
|
+
filePath: pathCheck.resolved,
|
|
259
|
+
appendedSize: content.length
|
|
260
|
+
}
|
|
261
|
+
} catch (error) {
|
|
262
|
+
return { success: false, error: error.message }
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
})
|
|
266
|
+
|
|
228
267
|
// 删除文件
|
|
229
268
|
framework.registerTool({
|
|
230
269
|
name: 'delete_file',
|
package/plugins/weixin-plugin.js
CHANGED
|
@@ -14,6 +14,8 @@ const {cleanResponse} =require('../src/utils')
|
|
|
14
14
|
const removeMarkdown = require('remove-markdown');
|
|
15
15
|
const { z } = require('zod')
|
|
16
16
|
const { WeixinBot } = require('@chnak/weixin-bot')
|
|
17
|
+
const fs = require('fs')
|
|
18
|
+
const path = require('path')
|
|
17
19
|
|
|
18
20
|
class WeixinPlugin extends Plugin {
|
|
19
21
|
constructor(config = {}) {
|
|
@@ -26,7 +28,7 @@ class WeixinPlugin extends Plugin {
|
|
|
26
28
|
this.enabled = false
|
|
27
29
|
this.path=`.agent/data`
|
|
28
30
|
this.systemPrompt=`你是一个微信助手。
|
|
29
|
-
|
|
31
|
+
|
|
30
32
|
**重要:** 子Agent 匹配规则必须遵守:
|
|
31
33
|
- 根据【子 Agent 匹配表】,将任务委托给最匹配的子Agent处理
|
|
32
34
|
- 使用 subagent_call 工具并指定 agentName 来委托任务
|
|
@@ -45,6 +47,7 @@ class WeixinPlugin extends Plugin {
|
|
|
45
47
|
|
|
46
48
|
this._framework = null
|
|
47
49
|
this._bot = null
|
|
50
|
+
this._myUserId = null // 机器人自己的 userId
|
|
48
51
|
this._sessionPlugin = null
|
|
49
52
|
this._sessionDeleteHandler = null
|
|
50
53
|
this._sessionAgents = new Map() // userId -> Agent
|
|
@@ -55,9 +58,190 @@ class WeixinPlugin extends Plugin {
|
|
|
55
58
|
|
|
56
59
|
install(framework) {
|
|
57
60
|
this._framework = framework
|
|
61
|
+
// 注册微信发送工具
|
|
62
|
+
this._registerTools()
|
|
58
63
|
return this
|
|
59
64
|
}
|
|
60
65
|
|
|
66
|
+
/**
|
|
67
|
+
* 注册微信发送工具(发送给自己)
|
|
68
|
+
*/
|
|
69
|
+
_registerTools() {
|
|
70
|
+
// 发送文本消息给自己
|
|
71
|
+
this.registerTool({
|
|
72
|
+
name: 'weixin_send_message',
|
|
73
|
+
description: '发送文本消息到微信(发给登录的账号)',
|
|
74
|
+
inputSchema: z.object({
|
|
75
|
+
message: z.string().describe('要发送的文本内容')
|
|
76
|
+
}),
|
|
77
|
+
execute: async (args) => {
|
|
78
|
+
return this._sendMessage(args.message)
|
|
79
|
+
}
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
// 发送图片给自己
|
|
83
|
+
this.registerTool({
|
|
84
|
+
name: 'weixin_send_image',
|
|
85
|
+
description: '发送图片到微信(发给登录的账号)',
|
|
86
|
+
inputSchema: z.object({
|
|
87
|
+
imagePath: z.string().describe('本地图片文件路径')
|
|
88
|
+
}),
|
|
89
|
+
execute: async (args) => {
|
|
90
|
+
return this._sendImage(args.imagePath)
|
|
91
|
+
}
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
// 发送文件给自己
|
|
95
|
+
this.registerTool({
|
|
96
|
+
name: 'weixin_send_file',
|
|
97
|
+
description: '发送文件到微信(发给登录的账号)',
|
|
98
|
+
inputSchema: z.object({
|
|
99
|
+
filePath: z.string().describe('本地文件路径'),
|
|
100
|
+
fileName: z.string().optional().describe('文件名(可选,默认从路径推断)')
|
|
101
|
+
}),
|
|
102
|
+
execute: async (args) => {
|
|
103
|
+
return this._sendFile(args.filePath, args.fileName)
|
|
104
|
+
}
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
// 发送视频给自己
|
|
108
|
+
this.registerTool({
|
|
109
|
+
name: 'weixin_send_video',
|
|
110
|
+
description: '发送视频到微信(发给登录的账号)',
|
|
111
|
+
inputSchema: z.object({
|
|
112
|
+
videoPath: z.string().describe('本地视频文件路径'),
|
|
113
|
+
playLength: z.number().optional().describe('视频时长(秒)')
|
|
114
|
+
}),
|
|
115
|
+
execute: async (args) => {
|
|
116
|
+
return this._sendVideo(args.videoPath, args.playLength)
|
|
117
|
+
}
|
|
118
|
+
})
|
|
119
|
+
|
|
120
|
+
// 发送语音给自己
|
|
121
|
+
this.registerTool({
|
|
122
|
+
name: 'weixin_send_voice',
|
|
123
|
+
description: '发送语音到微信(发给登录的账号,silk格式)',
|
|
124
|
+
inputSchema: z.object({
|
|
125
|
+
voicePath: z.string().describe('本地语音文件路径(silk格式)')
|
|
126
|
+
}),
|
|
127
|
+
execute: async (args) => {
|
|
128
|
+
return this._sendVoice(args.voicePath)
|
|
129
|
+
}
|
|
130
|
+
})
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* 发送图片给自己
|
|
135
|
+
*/
|
|
136
|
+
async _sendImage(imagePath) {
|
|
137
|
+
if (!this._bot) {
|
|
138
|
+
return { error: '微信机器人未连接' }
|
|
139
|
+
}
|
|
140
|
+
if (!this._myUserId) {
|
|
141
|
+
return { error: '未记录机器人用户ID' }
|
|
142
|
+
}
|
|
143
|
+
try {
|
|
144
|
+
if (!fs.existsSync(imagePath)) {
|
|
145
|
+
return { error: `文件不存在: ${imagePath}` }
|
|
146
|
+
}
|
|
147
|
+
await this._bot.sendImage(this._myUserId, imagePath)
|
|
148
|
+
// log.info(` 图片发送成功: ${path.basename(imagePath)}`)
|
|
149
|
+
return { success: true, message: '图片发送成功' }
|
|
150
|
+
} catch (err) {
|
|
151
|
+
log.error(` 图片发送失败: ${err.message}`)
|
|
152
|
+
return { error: err.message }
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* 发送文件给自己
|
|
158
|
+
*/
|
|
159
|
+
async _sendFile(filePath, fileName) {
|
|
160
|
+
if (!this._bot) {
|
|
161
|
+
return { error: '微信机器人未连接' }
|
|
162
|
+
}
|
|
163
|
+
if (!this._myUserId) {
|
|
164
|
+
return { error: '未记录机器人用户ID' }
|
|
165
|
+
}
|
|
166
|
+
try {
|
|
167
|
+
if (!fs.existsSync(filePath)) {
|
|
168
|
+
return { error: `文件不存在: ${filePath}` }
|
|
169
|
+
}
|
|
170
|
+
await this._bot.sendFile(this._myUserId, filePath, fileName)
|
|
171
|
+
// log.info(` 文件发送成功: ${fileName || path.basename(filePath)}`)
|
|
172
|
+
return { success: true, message: '文件发送成功' }
|
|
173
|
+
} catch (err) {
|
|
174
|
+
log.error(` 文件发送失败: ${err.message}`)
|
|
175
|
+
return { error: err.message }
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* 发送视频给自己
|
|
181
|
+
*/
|
|
182
|
+
async _sendVideo(videoPath, playLength) {
|
|
183
|
+
if (!this._bot) {
|
|
184
|
+
return { error: '微信机器人未连接' }
|
|
185
|
+
}
|
|
186
|
+
if (!this._myUserId) {
|
|
187
|
+
return { error: '未记录机器人用户ID' }
|
|
188
|
+
}
|
|
189
|
+
try {
|
|
190
|
+
if (!fs.existsSync(videoPath)) {
|
|
191
|
+
return { error: `文件不存在: ${videoPath}` }
|
|
192
|
+
}
|
|
193
|
+
const opts = playLength ? { playLength } : {}
|
|
194
|
+
await this._bot.sendVideo(this._myUserId, videoPath, opts)
|
|
195
|
+
// log.info(` 视频发送成功: ${path.basename(videoPath)}`)
|
|
196
|
+
return { success: true, message: '视频发送成功' }
|
|
197
|
+
} catch (err) {
|
|
198
|
+
log.error(` 视频发送失败: ${err.message}`)
|
|
199
|
+
return { error: err.message }
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* 发送语音给自己
|
|
205
|
+
*/
|
|
206
|
+
async _sendVoice(voicePath) {
|
|
207
|
+
if (!this._bot) {
|
|
208
|
+
return { error: '微信机器人未连接' }
|
|
209
|
+
}
|
|
210
|
+
if (!this._myUserId) {
|
|
211
|
+
return { error: '未记录机器人用户ID' }
|
|
212
|
+
}
|
|
213
|
+
try {
|
|
214
|
+
if (!fs.existsSync(voicePath)) {
|
|
215
|
+
return { error: `文件不存在: ${voicePath}` }
|
|
216
|
+
}
|
|
217
|
+
await this._bot.sendVoice(this._myUserId, voicePath)
|
|
218
|
+
// log.info(` 语音发送成功: ${path.basename(voicePath)}`)
|
|
219
|
+
return { success: true, message: '语音发送成功' }
|
|
220
|
+
} catch (err) {
|
|
221
|
+
log.error(` 语音发送失败: ${err.message}`)
|
|
222
|
+
return { error: err.message }
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* 发送文本消息给自己
|
|
228
|
+
*/
|
|
229
|
+
async _sendMessage(message) {
|
|
230
|
+
if (!this._bot) {
|
|
231
|
+
return { error: '微信机器人未连接' }
|
|
232
|
+
}
|
|
233
|
+
if (!this._myUserId) {
|
|
234
|
+
return { error: '未记录机器人用户ID' }
|
|
235
|
+
}
|
|
236
|
+
try {
|
|
237
|
+
await this._bot.send(this._myUserId, message)
|
|
238
|
+
return { success: true, message: '消息发送成功' }
|
|
239
|
+
} catch (err) {
|
|
240
|
+
log.error(` 消息发送失败: ${err.message}`)
|
|
241
|
+
return { error: err.message }
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
61
245
|
async start(framework) {
|
|
62
246
|
// 防止重复初始化
|
|
63
247
|
if (this._initialized) return this
|
|
@@ -71,7 +255,7 @@ class WeixinPlugin extends Plugin {
|
|
|
71
255
|
this._sessionDeleteHandler = (sessionId) => {
|
|
72
256
|
// 只处理 weixin 会话的删除
|
|
73
257
|
if (sessionId.startsWith('weixin_')) {
|
|
74
|
-
log.info(` Session deleted: ${sessionId}`)
|
|
258
|
+
// log.info(` Session deleted: ${sessionId}`)
|
|
75
259
|
}
|
|
76
260
|
}
|
|
77
261
|
this._sessionPlugin.on('session:deleted', this._sessionDeleteHandler)
|
|
@@ -80,19 +264,19 @@ class WeixinPlugin extends Plugin {
|
|
|
80
264
|
// 监听定时提醒事件
|
|
81
265
|
if (this._framework) {
|
|
82
266
|
this._framework.on('scheduler:reminder', async (data) => {
|
|
83
|
-
log.info(' Received scheduler reminder:', data)
|
|
267
|
+
// log.info(' Received scheduler reminder:', data)
|
|
84
268
|
await this._handleScheduledReminder(data)
|
|
85
269
|
})
|
|
86
270
|
|
|
87
271
|
// 监听 webhook 事件
|
|
88
272
|
this._framework.on('webhook:received', async (data) => {
|
|
89
|
-
log.info(' Received webhook event:', data)
|
|
273
|
+
// log.info(' Received webhook event:', data)
|
|
90
274
|
await this._handleWebhookNotification(data)
|
|
91
275
|
})
|
|
92
276
|
|
|
93
277
|
// 监听统一通知事件
|
|
94
278
|
this._framework.on('notification', async (data) => {
|
|
95
|
-
log.info(' Received notification:', data)
|
|
279
|
+
// log.info(' Received notification:', data)
|
|
96
280
|
await this._handleNotification(data)
|
|
97
281
|
})
|
|
98
282
|
}
|
|
@@ -119,17 +303,21 @@ class WeixinPlugin extends Plugin {
|
|
|
119
303
|
this._bot = new WeixinBot({
|
|
120
304
|
tokenPath:`${this.path}/${this.name}.json`,
|
|
121
305
|
onError: (err) => {
|
|
122
|
-
log.error(' Error:', err
|
|
306
|
+
log.error(' Error:', err.message)
|
|
123
307
|
},
|
|
124
308
|
})
|
|
125
309
|
|
|
126
310
|
const loginOptions = { force: this.config.forceLogin }
|
|
127
|
-
log.info('', this.config.forceLogin ? '强制重新扫码登录...' : '正在登录(已有凭证则自动跳过扫码)...')
|
|
311
|
+
// log.info('', this.config.forceLogin ? '强制重新扫码登录...' : '正在登录(已有凭证则自动跳过扫码)...')
|
|
128
312
|
|
|
129
313
|
const creds = await this._bot.login(loginOptions)
|
|
130
|
-
log.info(' 登录成功 — Bot ID:', creds.accountId)
|
|
131
|
-
log.info(' 关联用户:', creds.userId)
|
|
132
|
-
log.info(' API 地址:', creds.baseUrl)
|
|
314
|
+
// log.info(' 登录成功 — Bot ID:', creds.accountId)
|
|
315
|
+
// log.info(' 关联用户:', creds.userId)
|
|
316
|
+
// log.info(' API 地址:', creds.baseUrl)
|
|
317
|
+
|
|
318
|
+
// 记录机器人自己的 userId(用于发送媒体)
|
|
319
|
+
this._myUserId = creds.userId
|
|
320
|
+
// log.info(' 已记录机器人 userId:', this._myUserId)
|
|
133
321
|
|
|
134
322
|
// 注册消息处理
|
|
135
323
|
this._bot.onMessage(async (msg) => {
|
|
@@ -138,7 +326,7 @@ class WeixinPlugin extends Plugin {
|
|
|
138
326
|
|
|
139
327
|
// 启动Bot
|
|
140
328
|
await this._bot.run()
|
|
141
|
-
log.info(' 开始接收微信消息')
|
|
329
|
+
// log.info(' 开始接收微信消息')
|
|
142
330
|
}
|
|
143
331
|
|
|
144
332
|
/**
|
|
@@ -180,7 +368,7 @@ class WeixinPlugin extends Plugin {
|
|
|
180
368
|
// 检查缓存
|
|
181
369
|
if (this._sessionAgents.has(userId)) {
|
|
182
370
|
const agent = this._sessionAgents.get(userId)
|
|
183
|
-
log.info(' Reusing cached session agent for userId:', userId)
|
|
371
|
+
// log.info(' Reusing cached session agent for userId:', userId)
|
|
184
372
|
return { agent, sessionId: `weixin_${userId}` }
|
|
185
373
|
}
|
|
186
374
|
|
|
@@ -191,7 +379,7 @@ class WeixinPlugin extends Plugin {
|
|
|
191
379
|
metadata: { WORK_DIR: process.cwd() }
|
|
192
380
|
})
|
|
193
381
|
this._sessionAgents.set(userId, agent)
|
|
194
|
-
log.info(' Created new session agent for userId:', userId)
|
|
382
|
+
// log.info(' Created new session agent for userId:', userId)
|
|
195
383
|
|
|
196
384
|
// 使用 SessionPlugin 管理会话历史
|
|
197
385
|
if (this._sessionPlugin) {
|
|
@@ -201,7 +389,7 @@ class WeixinPlugin extends Plugin {
|
|
|
201
389
|
})
|
|
202
390
|
}
|
|
203
391
|
|
|
204
|
-
log.info(` Session ready for user: ${userId}`)
|
|
392
|
+
// log.info(` Session ready for user: ${userId}`)
|
|
205
393
|
return { agent, sessionId: `weixin_${userId}` }
|
|
206
394
|
}
|
|
207
395
|
|
|
@@ -219,17 +407,78 @@ class WeixinPlugin extends Plugin {
|
|
|
219
407
|
messageCount = session?.messages?.length || 0
|
|
220
408
|
}
|
|
221
409
|
|
|
222
|
-
log.info(` #${messageCount + 1} | 类型: ${msg.type} | 用户: ${userId}`)
|
|
223
|
-
|
|
410
|
+
//log.info(` #${messageCount + 1} | 类型: ${msg.type} | 用户: ${userId}`)
|
|
411
|
+
|
|
412
|
+
// 处理不同类型的消息
|
|
413
|
+
switch (msg.type) {
|
|
414
|
+
case 'text':
|
|
415
|
+
// log.info(` 内容: ${msg.text}`)
|
|
416
|
+
if (msg.text) {
|
|
417
|
+
await this._processChat(userId, msg.text.trim(), msg)
|
|
418
|
+
}
|
|
419
|
+
break
|
|
420
|
+
|
|
421
|
+
case 'image':
|
|
422
|
+
// log.info(` 收到图片消息`)
|
|
423
|
+
await this._processMediaChat(userId, '[用户发送了图片]', msg)
|
|
424
|
+
break
|
|
425
|
+
|
|
426
|
+
case 'file':
|
|
427
|
+
// log.info(` 收到文件消息: ${msg.fileName || 'unknown'}`)
|
|
428
|
+
await this._processMediaChat(userId, `[用户发送了文件: ${msg.fileName || 'unknown'}]`, msg)
|
|
429
|
+
break
|
|
430
|
+
|
|
431
|
+
case 'video':
|
|
432
|
+
// log.info(` 收到视频消息`)
|
|
433
|
+
await this._processMediaChat(userId, '[用户发送了视频]', msg)
|
|
434
|
+
break
|
|
435
|
+
|
|
436
|
+
case 'voice':
|
|
437
|
+
// log.info(` 收到语音消息`)
|
|
438
|
+
await this._processMediaChat(userId, '[用户发送了语音消息]', msg)
|
|
439
|
+
break
|
|
440
|
+
|
|
441
|
+
default:
|
|
442
|
+
// log.info(` 不支持的消息类型: ${msg.type}`)
|
|
443
|
+
if (msg.text) {
|
|
444
|
+
await this._processChat(userId, msg.text.trim(), msg)
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
}
|
|
224
448
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
449
|
+
/**
|
|
450
|
+
* 处理媒体消息(图片/文件/视频/语音)
|
|
451
|
+
*/
|
|
452
|
+
async _processMediaChat(userId, text, originalMsg) {
|
|
453
|
+
const sessionInfo = this._getSessionAgent(userId)
|
|
454
|
+
if (!sessionInfo) {
|
|
455
|
+
log.error(' No session agent available')
|
|
228
456
|
return
|
|
229
457
|
}
|
|
230
458
|
|
|
231
|
-
const
|
|
232
|
-
|
|
459
|
+
const { agent, sessionId } = sessionInfo
|
|
460
|
+
|
|
461
|
+
// 发送正在输入状态
|
|
462
|
+
try {
|
|
463
|
+
await this._bot.sendTyping(userId)
|
|
464
|
+
} catch { /* typing 失败不影响回复 */ }
|
|
465
|
+
|
|
466
|
+
await new Promise((resolve) => setTimeout(resolve, 1000))
|
|
467
|
+
|
|
468
|
+
try {
|
|
469
|
+
const result = await agent.chat(text, {
|
|
470
|
+
sessionId: sessionId
|
|
471
|
+
})
|
|
472
|
+
const fullResponse = cleanResponse(result.message || '')
|
|
473
|
+
|
|
474
|
+
if (fullResponse) {
|
|
475
|
+
await this._sendMessageBatch(originalMsg, userId, fullResponse, true)
|
|
476
|
+
// log.info(` 回复成功 (${fullResponse.length} 字符)`)
|
|
477
|
+
}
|
|
478
|
+
} catch (err) {
|
|
479
|
+
log.error(' Media chat error:', err)
|
|
480
|
+
await this._sendMessageBatch(originalMsg, userId, `发生错误:${err.message}`, true)
|
|
481
|
+
}
|
|
233
482
|
}
|
|
234
483
|
|
|
235
484
|
/**
|
|
@@ -271,13 +520,13 @@ class WeixinPlugin extends Plugin {
|
|
|
271
520
|
// 发送回复(超过500字自动分批)
|
|
272
521
|
if (fullResponse) {
|
|
273
522
|
await this._sendMessageBatch(originalMsg, userId, fullResponse, true)
|
|
274
|
-
log.info(` 回复成功 (${fullResponse.length} 字符)`)
|
|
523
|
+
// log.info(` 回复成功 (${fullResponse.length} 字符)`)
|
|
275
524
|
} else {
|
|
276
525
|
await this._sendMessageBatch(originalMsg, userId, '抱歉,我没有收到有效的回复。', true)
|
|
277
526
|
}
|
|
278
527
|
|
|
279
528
|
} catch (err) {
|
|
280
|
-
log.error(' Chat error:', err)
|
|
529
|
+
log.error(' Chat error:', err.message)
|
|
281
530
|
await this._sendMessageBatch(originalMsg, userId, `发生错误:${err.message}`, true)
|
|
282
531
|
}
|
|
283
532
|
}
|
|
@@ -317,7 +566,7 @@ class WeixinPlugin extends Plugin {
|
|
|
317
566
|
chunks.push(text.slice(i, i + MAX_LEN))
|
|
318
567
|
}
|
|
319
568
|
|
|
320
|
-
log.info(` Message too long (${text.length}), splitting into ${chunks.length} parts`)
|
|
569
|
+
// log.info(` Message too long (${text.length}), splitting into ${chunks.length} parts`)
|
|
321
570
|
|
|
322
571
|
for (let i = 0; i < chunks.length; i++) {
|
|
323
572
|
const chunk = `[${i + 1}/${chunks.length}]\n${chunks[i]}`
|
|
@@ -363,7 +612,7 @@ class WeixinPlugin extends Plugin {
|
|
|
363
612
|
const userId = sessionId.replace('weixin_', '')
|
|
364
613
|
try {
|
|
365
614
|
await this._sendMessageBatch(null, userId, reminderText, false)
|
|
366
|
-
log.info(` Reminder sent to user ${userId}`)
|
|
615
|
+
// log.info(` Reminder sent to user ${userId}`)
|
|
367
616
|
} catch (err) {
|
|
368
617
|
log.error(` Failed to send reminder:`, err.message)
|
|
369
618
|
}
|
|
@@ -381,7 +630,7 @@ class WeixinPlugin extends Plugin {
|
|
|
381
630
|
const userId = weixinSessions[0].id.replace('weixin_', '')
|
|
382
631
|
try {
|
|
383
632
|
await this._sendMessageBatch(null, userId, reminderText, false)
|
|
384
|
-
log.info(` Reminder sent to recent user ${userId}`)
|
|
633
|
+
// log.info(` Reminder sent to recent user ${userId}`)
|
|
385
634
|
} catch (err) {
|
|
386
635
|
log.error(` Failed to send reminder:`, err.message)
|
|
387
636
|
}
|
|
@@ -412,7 +661,7 @@ class WeixinPlugin extends Plugin {
|
|
|
412
661
|
|
|
413
662
|
try {
|
|
414
663
|
await this._sendMessageBatch(null, userId, notificationText, false)
|
|
415
|
-
log.info(` Webhook notification sent to user ${userId}`)
|
|
664
|
+
// log.info(` Webhook notification sent to user ${userId}`)
|
|
416
665
|
} catch (err) {
|
|
417
666
|
log.error(` Failed to send webhook notification:`, err.message)
|
|
418
667
|
}
|
|
@@ -470,7 +719,7 @@ class WeixinPlugin extends Plugin {
|
|
|
470
719
|
|
|
471
720
|
try {
|
|
472
721
|
await this._sendMessageBatch(null, userId, notificationText, false)
|
|
473
|
-
log.info(` Notification sent to user ${userId}`)
|
|
722
|
+
// log.info(` Notification sent to user ${userId}`)
|
|
474
723
|
} catch (err) {
|
|
475
724
|
log.error(` Failed to send notification:`, err.message)
|
|
476
725
|
}
|
|
@@ -511,7 +760,7 @@ class WeixinPlugin extends Plugin {
|
|
|
511
760
|
if (this._bot) {
|
|
512
761
|
this._bot.stop()
|
|
513
762
|
this._bot = null
|
|
514
|
-
log.info(' Bot stopped')
|
|
763
|
+
// log.info(' Bot stopped')
|
|
515
764
|
}
|
|
516
765
|
}
|
|
517
766
|
|