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.
Files changed (169) 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/test_plugin.py +304 -0
  27. package/.agent/plugins.json +14 -5
  28. package/.agent/python-scripts/test_sample.py +24 -0
  29. package/.agent/skills/agent-browser/SKILL.md +311 -0
  30. package/.agent/skills/agent-browser/TEST_PLAN.md +200 -0
  31. package/.agent/skills/sysinfo/SKILL.md +38 -0
  32. package/.agent/skills/sysinfo/system-info.sh +130 -0
  33. package/.agent/skills/workflow/SKILL.md +324 -0
  34. package/.agent/workflows/email-digest.json +50 -0
  35. package/.agent/workflows/file-backup.json +21 -0
  36. package/.agent/workflows/get-ip-notify.json +32 -0
  37. package/.agent/workflows/news-aggregator.json +93 -0
  38. package/.agent/workflows/news-dashboard-v2.json +94 -0
  39. package/.agent/workflows/notification-batch.json +32 -0
  40. package/.claude/settings.local.json +171 -178
  41. package/.env.example +56 -56
  42. package/README.md +441 -441
  43. package/package.json +2 -2
  44. package/plugins/ambient-agent/EventWatcher.js +4 -4
  45. package/plugins/extension-executor-plugin.js +44 -1
  46. package/plugins/file-system-plugin.js +44 -5
  47. package/plugins/weixin-plugin.js +278 -29
  48. package/skills/find-skills/AGENTS.md +162 -162
  49. package/skills/find-skills/SKILL.md +133 -133
  50. package/skills/foliko-dev/SKILL.md +67 -0
  51. package/skills/python-plugin-dev/SKILL.md +238 -238
  52. package/src/core/agent-chat.js +4 -3
  53. package/.agent/.shared/ui-ux-pro-max/data/charts.csv +0 -26
  54. package/.agent/.shared/ui-ux-pro-max/data/colors.csv +0 -97
  55. package/.agent/.shared/ui-ux-pro-max/data/icons.csv +0 -101
  56. package/.agent/.shared/ui-ux-pro-max/data/landing.csv +0 -31
  57. package/.agent/.shared/ui-ux-pro-max/data/products.csv +0 -97
  58. package/.agent/.shared/ui-ux-pro-max/data/prompts.csv +0 -24
  59. package/.agent/.shared/ui-ux-pro-max/data/react-performance.csv +0 -45
  60. package/.agent/.shared/ui-ux-pro-max/data/stacks/flutter.csv +0 -53
  61. package/.agent/.shared/ui-ux-pro-max/data/stacks/html-tailwind.csv +0 -56
  62. package/.agent/.shared/ui-ux-pro-max/data/stacks/jetpack-compose.csv +0 -53
  63. package/.agent/.shared/ui-ux-pro-max/data/stacks/nextjs.csv +0 -53
  64. package/.agent/.shared/ui-ux-pro-max/data/stacks/nuxt-ui.csv +0 -51
  65. package/.agent/.shared/ui-ux-pro-max/data/stacks/nuxtjs.csv +0 -59
  66. package/.agent/.shared/ui-ux-pro-max/data/stacks/react-native.csv +0 -52
  67. package/.agent/.shared/ui-ux-pro-max/data/stacks/react.csv +0 -54
  68. package/.agent/.shared/ui-ux-pro-max/data/stacks/shadcn.csv +0 -61
  69. package/.agent/.shared/ui-ux-pro-max/data/stacks/svelte.csv +0 -54
  70. package/.agent/.shared/ui-ux-pro-max/data/stacks/swiftui.csv +0 -51
  71. package/.agent/.shared/ui-ux-pro-max/data/stacks/vue.csv +0 -50
  72. package/.agent/.shared/ui-ux-pro-max/data/styles.csv +0 -59
  73. package/.agent/.shared/ui-ux-pro-max/data/typography.csv +0 -58
  74. package/.agent/.shared/ui-ux-pro-max/data/ui-reasoning.csv +0 -101
  75. package/.agent/.shared/ui-ux-pro-max/data/ux-guidelines.csv +0 -100
  76. package/.agent/.shared/ui-ux-pro-max/data/web-interface.csv +0 -31
  77. package/.agent/.shared/ui-ux-pro-max/scripts/__pycache__/core.cpython-313.pyc +0 -0
  78. package/.agent/.shared/ui-ux-pro-max/scripts/__pycache__/design_system.cpython-313.pyc +0 -0
  79. package/.agent/.shared/ui-ux-pro-max/scripts/core.py +0 -258
  80. package/.agent/.shared/ui-ux-pro-max/scripts/design_system.py +0 -1067
  81. package/.agent/.shared/ui-ux-pro-max/scripts/search.py +0 -106
  82. package/.agent/ARCHITECTURE.md +0 -288
  83. package/.agent/agents/ambient-agent.md +0 -57
  84. package/.agent/agents/debugger.md +0 -55
  85. package/.agent/agents/email-assistant.md +0 -49
  86. package/.agent/agents/file-manager.md +0 -42
  87. package/.agent/agents/python-developer.md +0 -60
  88. package/.agent/agents/scheduler.md +0 -59
  89. package/.agent/agents/web-developer.md +0 -45
  90. package/.agent/data/puppeteer-sessions/undefined.json +0 -6
  91. package/.agent/mcp_config_updated.json +0 -12
  92. package/.agent/rules/GEMINI.md +0 -273
  93. package/.agent/rules/allow-rule.md +0 -77
  94. package/.agent/rules/log-rule.md +0 -83
  95. package/.agent/rules/security-rule.md +0 -93
  96. package/.agent/scripts/auto_preview.py +0 -148
  97. package/.agent/scripts/checklist.py +0 -217
  98. package/.agent/scripts/session_manager.py +0 -120
  99. package/.agent/scripts/verify_all.py +0 -327
  100. package/.agent/skills/api-patterns/SKILL.md +0 -81
  101. package/.agent/skills/api-patterns/api-style.md +0 -42
  102. package/.agent/skills/api-patterns/auth.md +0 -24
  103. package/.agent/skills/api-patterns/documentation.md +0 -26
  104. package/.agent/skills/api-patterns/graphql.md +0 -41
  105. package/.agent/skills/api-patterns/rate-limiting.md +0 -31
  106. package/.agent/skills/api-patterns/response.md +0 -37
  107. package/.agent/skills/api-patterns/rest.md +0 -40
  108. package/.agent/skills/api-patterns/scripts/api_validator.py +0 -211
  109. package/.agent/skills/api-patterns/security-testing.md +0 -122
  110. package/.agent/skills/api-patterns/trpc.md +0 -41
  111. package/.agent/skills/api-patterns/versioning.md +0 -22
  112. package/.agent/skills/app-builder/SKILL.md +0 -75
  113. package/.agent/skills/app-builder/agent-coordination.md +0 -71
  114. package/.agent/skills/app-builder/feature-building.md +0 -53
  115. package/.agent/skills/app-builder/project-detection.md +0 -34
  116. package/.agent/skills/app-builder/scaffolding.md +0 -118
  117. package/.agent/skills/app-builder/tech-stack.md +0 -40
  118. package/.agent/skills/app-builder/templates/SKILL.md +0 -39
  119. package/.agent/skills/app-builder/templates/astro-static/TEMPLATE.md +0 -76
  120. package/.agent/skills/app-builder/templates/chrome-extension/TEMPLATE.md +0 -92
  121. package/.agent/skills/app-builder/templates/cli-tool/TEMPLATE.md +0 -88
  122. package/.agent/skills/app-builder/templates/electron-desktop/TEMPLATE.md +0 -88
  123. package/.agent/skills/app-builder/templates/express-api/TEMPLATE.md +0 -83
  124. package/.agent/skills/app-builder/templates/flutter-app/TEMPLATE.md +0 -90
  125. package/.agent/skills/app-builder/templates/monorepo-turborepo/TEMPLATE.md +0 -90
  126. package/.agent/skills/app-builder/templates/nextjs-fullstack/TEMPLATE.md +0 -122
  127. package/.agent/skills/app-builder/templates/nextjs-saas/TEMPLATE.md +0 -122
  128. package/.agent/skills/app-builder/templates/nextjs-static/TEMPLATE.md +0 -169
  129. package/.agent/skills/app-builder/templates/nuxt-app/TEMPLATE.md +0 -134
  130. package/.agent/skills/app-builder/templates/python-fastapi/TEMPLATE.md +0 -83
  131. package/.agent/skills/app-builder/templates/react-native-app/TEMPLATE.md +0 -119
  132. package/.agent/skills/architecture/SKILL.md +0 -55
  133. package/.agent/skills/architecture/context-discovery.md +0 -43
  134. package/.agent/skills/architecture/examples.md +0 -94
  135. package/.agent/skills/architecture/pattern-selection.md +0 -68
  136. package/.agent/skills/architecture/patterns-reference.md +0 -50
  137. package/.agent/skills/architecture/trade-off-analysis.md +0 -77
  138. package/.agent/skills/clean-code/SKILL.md +0 -201
  139. package/.agent/skills/doc.md +0 -177
  140. package/.agent/skills/frontend-design/SKILL.md +0 -418
  141. package/.agent/skills/frontend-design/animation-guide.md +0 -331
  142. package/.agent/skills/frontend-design/color-system.md +0 -311
  143. package/.agent/skills/frontend-design/decision-trees.md +0 -418
  144. package/.agent/skills/frontend-design/motion-graphics.md +0 -306
  145. package/.agent/skills/frontend-design/scripts/accessibility_checker.py +0 -183
  146. package/.agent/skills/frontend-design/scripts/ux_audit.py +0 -722
  147. package/.agent/skills/frontend-design/typography-system.md +0 -345
  148. package/.agent/skills/frontend-design/ux-psychology.md +0 -1116
  149. package/.agent/skills/frontend-design/visual-effects.md +0 -383
  150. package/.agent/skills/i18n-localization/SKILL.md +0 -154
  151. package/.agent/skills/i18n-localization/scripts/i18n_checker.py +0 -241
  152. package/.agent/skills/mcp-builder/SKILL.md +0 -176
  153. package/.agent/skills/web-design-guidelines/SKILL.md +0 -57
  154. package/.agent/workflows/brainstorm.md +0 -113
  155. package/.agent/workflows/create.md +0 -59
  156. package/.agent/workflows/debug.md +0 -103
  157. package/.agent/workflows/deploy.md +0 -176
  158. package/.agent/workflows/enhance.md +0 -63
  159. package/.agent/workflows/orchestrate.md +0 -237
  160. package/.agent/workflows/plan.md +0 -89
  161. package/.agent/workflows/preview.md +0 -81
  162. package/.agent/workflows/simple-test.md +0 -42
  163. package/.agent/workflows/status.md +0 -86
  164. package/.agent/workflows/structured-orchestrate.md +0 -180
  165. package/.agent/workflows/test.md +0 -144
  166. package/.agent/workflows/ui-ux-pro-max.md +0 -296
  167. /package/.agent/plugins/{puppeteer-plugin → temp-repo/puppeteer-plugin}/README.md +0 -0
  168. /package/.agent/plugins/{puppeteer-plugin → temp-repo/puppeteer-plugin}/index.js +0 -0
  169. /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.85",
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 || '',
@@ -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',
@@ -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