foliko 1.0.84 → 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.
Files changed (170) hide show
  1. package/.agent/agents/code-assistant.json +14 -0
  2. package/.agent/agents/email-assistant.json +14 -0
  3. package/.agent/agents/file-assistant.json +15 -0
  4. package/.agent/agents/system-assistant.json +15 -0
  5. package/.agent/agents/web-assistant.json +12 -0
  6. package/.agent/data/ambient/goals.json +50 -0
  7. package/.agent/data/ambient/memories.json +7 -0
  8. package/.agent/data/default.json +4156 -244
  9. package/.agent/data/plugins-state.json +162 -174
  10. package/.agent/data/scheduler/tasks.json +1 -0
  11. package/.agent/data/weixin.json +6 -0
  12. package/.agent/mcp_config.json +1 -0
  13. package/.agent/package.json +8 -0
  14. package/.agent/plugins/__pycache__/test_plugin.cpython-312.pyc +0 -0
  15. package/.agent/plugins/daytona/README.md +89 -0
  16. package/.agent/plugins/daytona/index.js +377 -0
  17. package/.agent/plugins/daytona/package.json +12 -0
  18. package/.agent/plugins/marknative/README.md +134 -0
  19. package/.agent/plugins/marknative/index.js +233 -0
  20. package/.agent/plugins/marknative/package.json +12 -0
  21. package/.agent/plugins/marknative/update-readme.js +134 -0
  22. package/.agent/plugins/system-info/index.js +387 -0
  23. package/.agent/plugins/system-info/package.json +4 -0
  24. package/.agent/plugins/system-info/test.js +40 -0
  25. package/.agent/plugins/temp-repo/LICENSE +201 -0
  26. package/.agent/plugins/{puppeteer-plugin → temp-repo/puppeteer-plugin}/index.js +101 -3
  27. package/.agent/plugins/test_plugin.py +304 -0
  28. package/.agent/plugins.json +14 -5
  29. package/.agent/python-scripts/test_sample.py +24 -0
  30. package/.agent/skills/agent-browser/SKILL.md +311 -0
  31. package/.agent/skills/agent-browser/TEST_PLAN.md +200 -0
  32. package/.agent/skills/sysinfo/SKILL.md +38 -0
  33. package/.agent/skills/sysinfo/system-info.sh +130 -0
  34. package/.agent/skills/workflow/SKILL.md +324 -0
  35. package/.agent/workflows/email-digest.json +50 -0
  36. package/.agent/workflows/file-backup.json +21 -0
  37. package/.agent/workflows/get-ip-notify.json +32 -0
  38. package/.agent/workflows/news-aggregator.json +93 -0
  39. package/.agent/workflows/news-dashboard-v2.json +94 -0
  40. package/.agent/workflows/notification-batch.json +32 -0
  41. package/.claude/settings.local.json +171 -178
  42. package/.env.example +56 -56
  43. package/README.md +441 -441
  44. package/package.json +2 -2
  45. package/plugins/ambient-agent/EventWatcher.js +4 -4
  46. package/plugins/extension-executor-plugin.js +52 -1
  47. package/plugins/file-system-plugin.js +44 -5
  48. package/plugins/session-plugin.js +3 -3
  49. package/plugins/weixin-plugin.js +278 -29
  50. package/skills/find-skills/AGENTS.md +162 -162
  51. package/skills/find-skills/SKILL.md +133 -133
  52. package/skills/foliko-dev/SKILL.md +67 -0
  53. package/skills/python-plugin-dev/SKILL.md +238 -238
  54. package/src/core/agent-chat.js +11 -21
  55. package/.agent/.shared/ui-ux-pro-max/data/charts.csv +0 -26
  56. package/.agent/.shared/ui-ux-pro-max/data/colors.csv +0 -97
  57. package/.agent/.shared/ui-ux-pro-max/data/icons.csv +0 -101
  58. package/.agent/.shared/ui-ux-pro-max/data/landing.csv +0 -31
  59. package/.agent/.shared/ui-ux-pro-max/data/products.csv +0 -97
  60. package/.agent/.shared/ui-ux-pro-max/data/prompts.csv +0 -24
  61. package/.agent/.shared/ui-ux-pro-max/data/react-performance.csv +0 -45
  62. package/.agent/.shared/ui-ux-pro-max/data/stacks/flutter.csv +0 -53
  63. package/.agent/.shared/ui-ux-pro-max/data/stacks/html-tailwind.csv +0 -56
  64. package/.agent/.shared/ui-ux-pro-max/data/stacks/jetpack-compose.csv +0 -53
  65. package/.agent/.shared/ui-ux-pro-max/data/stacks/nextjs.csv +0 -53
  66. package/.agent/.shared/ui-ux-pro-max/data/stacks/nuxt-ui.csv +0 -51
  67. package/.agent/.shared/ui-ux-pro-max/data/stacks/nuxtjs.csv +0 -59
  68. package/.agent/.shared/ui-ux-pro-max/data/stacks/react-native.csv +0 -52
  69. package/.agent/.shared/ui-ux-pro-max/data/stacks/react.csv +0 -54
  70. package/.agent/.shared/ui-ux-pro-max/data/stacks/shadcn.csv +0 -61
  71. package/.agent/.shared/ui-ux-pro-max/data/stacks/svelte.csv +0 -54
  72. package/.agent/.shared/ui-ux-pro-max/data/stacks/swiftui.csv +0 -51
  73. package/.agent/.shared/ui-ux-pro-max/data/stacks/vue.csv +0 -50
  74. package/.agent/.shared/ui-ux-pro-max/data/styles.csv +0 -59
  75. package/.agent/.shared/ui-ux-pro-max/data/typography.csv +0 -58
  76. package/.agent/.shared/ui-ux-pro-max/data/ui-reasoning.csv +0 -101
  77. package/.agent/.shared/ui-ux-pro-max/data/ux-guidelines.csv +0 -100
  78. package/.agent/.shared/ui-ux-pro-max/data/web-interface.csv +0 -31
  79. package/.agent/.shared/ui-ux-pro-max/scripts/__pycache__/core.cpython-313.pyc +0 -0
  80. package/.agent/.shared/ui-ux-pro-max/scripts/__pycache__/design_system.cpython-313.pyc +0 -0
  81. package/.agent/.shared/ui-ux-pro-max/scripts/core.py +0 -258
  82. package/.agent/.shared/ui-ux-pro-max/scripts/design_system.py +0 -1067
  83. package/.agent/.shared/ui-ux-pro-max/scripts/search.py +0 -106
  84. package/.agent/ARCHITECTURE.md +0 -288
  85. package/.agent/agents/ambient-agent.md +0 -57
  86. package/.agent/agents/debugger.md +0 -55
  87. package/.agent/agents/email-assistant.md +0 -49
  88. package/.agent/agents/file-manager.md +0 -42
  89. package/.agent/agents/python-developer.md +0 -60
  90. package/.agent/agents/scheduler.md +0 -59
  91. package/.agent/agents/web-developer.md +0 -45
  92. package/.agent/data/puppeteer-sessions/undefined.json +0 -6
  93. package/.agent/mcp_config_updated.json +0 -12
  94. package/.agent/rules/GEMINI.md +0 -273
  95. package/.agent/rules/allow-rule.md +0 -77
  96. package/.agent/rules/log-rule.md +0 -83
  97. package/.agent/rules/security-rule.md +0 -93
  98. package/.agent/scripts/auto_preview.py +0 -148
  99. package/.agent/scripts/checklist.py +0 -217
  100. package/.agent/scripts/session_manager.py +0 -120
  101. package/.agent/scripts/verify_all.py +0 -327
  102. package/.agent/skills/api-patterns/SKILL.md +0 -81
  103. package/.agent/skills/api-patterns/api-style.md +0 -42
  104. package/.agent/skills/api-patterns/auth.md +0 -24
  105. package/.agent/skills/api-patterns/documentation.md +0 -26
  106. package/.agent/skills/api-patterns/graphql.md +0 -41
  107. package/.agent/skills/api-patterns/rate-limiting.md +0 -31
  108. package/.agent/skills/api-patterns/response.md +0 -37
  109. package/.agent/skills/api-patterns/rest.md +0 -40
  110. package/.agent/skills/api-patterns/scripts/api_validator.py +0 -211
  111. package/.agent/skills/api-patterns/security-testing.md +0 -122
  112. package/.agent/skills/api-patterns/trpc.md +0 -41
  113. package/.agent/skills/api-patterns/versioning.md +0 -22
  114. package/.agent/skills/app-builder/SKILL.md +0 -75
  115. package/.agent/skills/app-builder/agent-coordination.md +0 -71
  116. package/.agent/skills/app-builder/feature-building.md +0 -53
  117. package/.agent/skills/app-builder/project-detection.md +0 -34
  118. package/.agent/skills/app-builder/scaffolding.md +0 -118
  119. package/.agent/skills/app-builder/tech-stack.md +0 -40
  120. package/.agent/skills/app-builder/templates/SKILL.md +0 -39
  121. package/.agent/skills/app-builder/templates/astro-static/TEMPLATE.md +0 -76
  122. package/.agent/skills/app-builder/templates/chrome-extension/TEMPLATE.md +0 -92
  123. package/.agent/skills/app-builder/templates/cli-tool/TEMPLATE.md +0 -88
  124. package/.agent/skills/app-builder/templates/electron-desktop/TEMPLATE.md +0 -88
  125. package/.agent/skills/app-builder/templates/express-api/TEMPLATE.md +0 -83
  126. package/.agent/skills/app-builder/templates/flutter-app/TEMPLATE.md +0 -90
  127. package/.agent/skills/app-builder/templates/monorepo-turborepo/TEMPLATE.md +0 -90
  128. package/.agent/skills/app-builder/templates/nextjs-fullstack/TEMPLATE.md +0 -122
  129. package/.agent/skills/app-builder/templates/nextjs-saas/TEMPLATE.md +0 -122
  130. package/.agent/skills/app-builder/templates/nextjs-static/TEMPLATE.md +0 -169
  131. package/.agent/skills/app-builder/templates/nuxt-app/TEMPLATE.md +0 -134
  132. package/.agent/skills/app-builder/templates/python-fastapi/TEMPLATE.md +0 -83
  133. package/.agent/skills/app-builder/templates/react-native-app/TEMPLATE.md +0 -119
  134. package/.agent/skills/architecture/SKILL.md +0 -55
  135. package/.agent/skills/architecture/context-discovery.md +0 -43
  136. package/.agent/skills/architecture/examples.md +0 -94
  137. package/.agent/skills/architecture/pattern-selection.md +0 -68
  138. package/.agent/skills/architecture/patterns-reference.md +0 -50
  139. package/.agent/skills/architecture/trade-off-analysis.md +0 -77
  140. package/.agent/skills/clean-code/SKILL.md +0 -201
  141. package/.agent/skills/doc.md +0 -177
  142. package/.agent/skills/frontend-design/SKILL.md +0 -418
  143. package/.agent/skills/frontend-design/animation-guide.md +0 -331
  144. package/.agent/skills/frontend-design/color-system.md +0 -311
  145. package/.agent/skills/frontend-design/decision-trees.md +0 -418
  146. package/.agent/skills/frontend-design/motion-graphics.md +0 -306
  147. package/.agent/skills/frontend-design/scripts/accessibility_checker.py +0 -183
  148. package/.agent/skills/frontend-design/scripts/ux_audit.py +0 -722
  149. package/.agent/skills/frontend-design/typography-system.md +0 -345
  150. package/.agent/skills/frontend-design/ux-psychology.md +0 -1116
  151. package/.agent/skills/frontend-design/visual-effects.md +0 -383
  152. package/.agent/skills/i18n-localization/SKILL.md +0 -154
  153. package/.agent/skills/i18n-localization/scripts/i18n_checker.py +0 -241
  154. package/.agent/skills/mcp-builder/SKILL.md +0 -176
  155. package/.agent/skills/web-design-guidelines/SKILL.md +0 -57
  156. package/.agent/workflows/brainstorm.md +0 -113
  157. package/.agent/workflows/create.md +0 -59
  158. package/.agent/workflows/debug.md +0 -103
  159. package/.agent/workflows/deploy.md +0 -176
  160. package/.agent/workflows/enhance.md +0 -63
  161. package/.agent/workflows/orchestrate.md +0 -237
  162. package/.agent/workflows/plan.md +0 -89
  163. package/.agent/workflows/preview.md +0 -81
  164. package/.agent/workflows/simple-test.md +0 -42
  165. package/.agent/workflows/status.md +0 -86
  166. package/.agent/workflows/structured-orchestrate.md +0 -180
  167. package/.agent/workflows/test.md +0 -144
  168. package/.agent/workflows/ui-ux-pro-max.md +0 -296
  169. /package/.agent/plugins/{puppeteer-plugin → temp-repo/puppeteer-plugin}/README.md +0 -0
  170. /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.84",
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.1",
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
- if (!ext.tools.find((t) => t.name === toolDef.name)) {
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 || '',
@@ -345,6 +388,14 @@ class ExtensionExecutorPlugin extends Plugin {
345
388
 
346
389
  reload(framework) {
347
390
  this._framework = framework;
391
+ // 重新扫描所有已加载插件的 tools
392
+ this._extensions.clear();
393
+ const plugins = framework.pluginManager.getAll();
394
+ for (const { instance: plugin } of plugins) {
395
+ this._scanPluginTools(plugin);
396
+ }
397
+ // 刷新所有 Agent 的扩展提示词
398
+ this._refreshAllAgentsExtPrompt(framework);
348
399
  }
349
400
 
350
401
  async uninstall(framework) {
@@ -154,11 +154,10 @@ class FileSystemPlugin extends Plugin {
154
154
  // 写入文件
155
155
  framework.registerTool({
156
156
  name: 'write_file',
157
- description: `创建或写入文件内容。
158
-
159
- 注意:
160
- - 对于大文件(>100MB),建议分多次调用 append_to_file
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',
@@ -272,9 +272,9 @@ class SessionPlugin extends Plugin {
272
272
  }
273
273
 
274
274
  // 如果会话数超过限制,清理最老的
275
- if (this._sessions.size >= this.config.maxSessions) {
276
- this._cleanupLRU()
277
- }
275
+ // if (this._sessions.size >= this.config.maxSessions) {
276
+ // this._cleanupLRU()
277
+ // }
278
278
 
279
279
  const session = {
280
280
  id,
@@ -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 instanceof Error ? err.stack ?? err.message : String(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
- log.info(` 内容: ${msg.text}`)
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
- if (msg.type !== 'text' || !msg.text) {
227
- log.info(' Unsupported message type or no text')
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 text = msg.text.trim()
232
- await this._processChat(userId, text, msg)
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