foliko 1.0.3 → 1.0.4

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.
@@ -26,7 +26,9 @@
26
26
  "Bash(node -e \":*)",
27
27
  "Bash(node -e \"\nconst { loadAgentConfig } = require\\('./plugins/default-plugins'\\);\nconst config = loadAgentConfig\\('D:/Date/20260321/app/.agent'\\);\nconsole.log\\('skillsDirs:', config.skillsDirs\\);\n\")",
28
28
  "Bash(cd D:/Code/vb-agent && pnpm install --shamefully-hoist 2>&1 | head -20)",
29
- "Bash(cd D:/Code/vb-agent && rm -rf node_modules && pnpm install)"
29
+ "Bash(cd D:/Code/vb-agent && rm -rf node_modules && pnpm install)",
30
+ "Bash(cd D:/Code/vb-agent && timeout 8 node test-tg.js 2>&1 || true)",
31
+ "Bash(cd D:/Code/vb-agent && timeout 10 node test-tg.js 2>&1 || true)"
30
32
  ]
31
33
  }
32
34
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "foliko",
3
- "version": "1.0.3",
3
+ "version": "1.0.4",
4
4
  "description": "简约的插件化 Agent 框架",
5
5
  "main": "src/index.js",
6
6
  "bin": {
@@ -31,6 +31,7 @@
31
31
  "marked": "^11.2.0",
32
32
  "marked-terminal": "6",
33
33
  "node-cron": "^4.2.1",
34
+ "node-telegram-bot-api": "^0.67.0",
34
35
  "zod": "^3.24.0"
35
36
  }
36
37
  }
@@ -28,11 +28,12 @@ class AIPlugin extends Plugin {
28
28
 
29
29
  install(framework) {
30
30
  this._framework = framework
31
+ this._initAIClient()
31
32
  return this
32
33
  }
33
34
 
34
35
  start(framework) {
35
- this._initAIClient()
36
+ // AI client 已在 install 中初始化
36
37
  return this
37
38
  }
38
39
 
@@ -212,6 +212,25 @@ async function bootstrapDefaults(framework, config = {}) {
212
212
  console.log('[Bootstrap] AI Plugin loaded')
213
213
  }
214
214
 
215
+ // 1.5 创建主 Agent(供 Telegram 等需要绑定 Agent 的插件使用)
216
+ if (!framework._mainAgent && aiConfig.provider) {
217
+ const { Agent } = require('../src/core/agent')
218
+ const aiPlugin = framework.pluginManager.get('ai')
219
+ const aiClient = aiPlugin ? aiPlugin.getAIClient() : null
220
+ console.log('[Bootstrap] Creating Main Agent - aiClient:', !!aiClient)
221
+
222
+ framework._mainAgent = new Agent(framework, {
223
+ name: 'MainAgent',
224
+ systemPrompt: '你是一个有帮助的AI助手。',
225
+ model: aiConfig.model,
226
+ provider: aiConfig.provider,
227
+ apiKey: aiConfig.apiKey,
228
+ baseURL: aiConfig.baseURL
229
+ })
230
+ framework._agents.push(framework._mainAgent)
231
+ console.log('[Bootstrap] Main Agent created, has _chatHandler:', !!framework._mainAgent._chatHandler)
232
+ }
233
+
215
234
  // 2. Storage 存储插件
216
235
  if (!framework.pluginManager.has('storage')) {
217
236
  const { StoragePlugin } = require('./storage-plugin')
@@ -351,6 +370,20 @@ async function bootstrapDefaults(framework, config = {}) {
351
370
  console.log('[Bootstrap] Python Plugin Loader already loaded, skipping')
352
371
  }
353
372
 
373
+ // 12.6 Telegram 插件(如果配置了Token)
374
+ if (!framework.pluginManager.has('telegram')) {
375
+ const telegramToken = process.env.TELEGRAM_BOT_TOKEN || agentConfig.telegram?.botToken
376
+ if (telegramToken) {
377
+ const { Plugin } = require('../src/core/plugin-base')
378
+ const createTelegramPlugin = require('./telegram-plugin')
379
+ const TelegramPlugin = createTelegramPlugin(Plugin)
380
+ await framework.loadPlugin(new TelegramPlugin({ botToken: telegramToken }))
381
+ console.log('[Bootstrap] Telegram Plugin loaded')
382
+ }
383
+ } else {
384
+ console.log('[Bootstrap] Telegram Plugin already loaded, skipping')
385
+ }
386
+
354
387
  // 13. 加载自定义插件
355
388
  await loadCustomPlugins(framework, agentConfig)
356
389
 
@@ -0,0 +1,387 @@
1
+ /**
2
+ * Telegram 插件 v2
3
+ * 支持绑定主Agent进行持续对话
4
+ *
5
+ * 配置:
6
+ * - botToken: Telegram Bot Token (必需)
7
+ * - allowedChats: 允许的聊天ID数组
8
+ * - groupMode: 是否启用群组模式
9
+ * - prefix: 命令前缀
10
+ */
11
+
12
+ const { Plugin } = require('../src/core/plugin-base')
13
+ const { z } = require('zod')
14
+
15
+ module.exports = function(Plugin) {
16
+ return class TelegramPlugin extends Plugin {
17
+ constructor(config = {}) {
18
+ super()
19
+ this.name = 'telegram'
20
+ this.version = '2.0.0'
21
+ this.description = 'Telegram 对话插件,绑定主Agent进行持续对话'
22
+ this.priority = 80
23
+
24
+ this.config = {
25
+ botToken: config.botToken || process.env.TELEGRAM_BOT_TOKEN,
26
+ allowedChats: config.allowedChats || [],
27
+ groupMode: config.groupMode || false,
28
+ prefix: config.prefix || '/',
29
+ systemPrompt: config.systemPrompt || '你是一个有帮助的AI助手。'
30
+ }
31
+
32
+ this._framework = null
33
+ this._bot = null
34
+ this._sessions = new Map() // chatId -> { agent, history, lastActive }
35
+ this._streamingMessages = new Map() // chatId -> messageId
36
+ }
37
+
38
+ install(framework) {
39
+ this._framework = framework
40
+ return this
41
+ }
42
+
43
+ start(framework) {
44
+ if (!this.config.botToken) {
45
+ console.warn('[Telegram] No bot token. Set TELEGRAM_BOT_TOKEN env var.')
46
+ return this
47
+ }
48
+
49
+ this._initBot()
50
+ return this
51
+ }
52
+
53
+ _initBot() {
54
+ try {
55
+ // 添加 .agent/node_modules 到搜索路径
56
+ // const path = require('path')
57
+ // const agentNodeModules = path.join(process.cwd(), '.agent', 'node_modules')
58
+ // if (!module.paths.includes(agentNodeModules)) {
59
+ // module.paths.unshift(agentNodeModules)
60
+ // }
61
+
62
+ const TelegramBot = require('node-telegram-bot-api')
63
+ this._bot = new TelegramBot(this.config.botToken, { polling: true })
64
+
65
+ console.log('[Telegram] Bot started successfully')
66
+
67
+ // 注册菜单命令
68
+ this._bot.setMyCommands([
69
+ { command: 'start', description: '显示帮助信息' },
70
+ { command: 'clear', description: '清除对话历史' },
71
+ { command: 'history', description: '查看对话状态' }
72
+ ]).then(() => {
73
+ console.log('[Telegram] Commands registered')
74
+ }).catch((err) => {
75
+ console.error('[Telegram] Failed to register commands:', err.message)
76
+ })
77
+
78
+ // 监听消息
79
+ this._bot.on('message', (msg) => this._handleMessage(msg))
80
+
81
+ // 监听编辑消息
82
+ this._bot.on('edit_message', (msg) => this._handleEdit(msg))
83
+
84
+ // 监听回调
85
+ this._bot.on('callback_query', (query) => this._handleCallback(query))
86
+
87
+ // 监听错误
88
+ this._bot.on('polling_error', (err) => {
89
+ console.error('[Telegram] Polling error:', err.message)
90
+ })
91
+
92
+ this._bot.on('error', (err) => {
93
+ console.error('[Telegram] Bot error:', err.message)
94
+ })
95
+
96
+ // 监听 Agent 创建事件,确保在 Agent 创建后能正确获取
97
+ if (this._framework) {
98
+ this._framework.on('agent:created', (agent) => {
99
+ console.log('[Telegram] New agent created:', agent.name)
100
+ })
101
+ }
102
+
103
+ } catch (err) {
104
+ console.error('[Telegram] Failed to initialize bot:', err.message)
105
+ }
106
+ }
107
+
108
+ /**
109
+ * 获取主Agent
110
+ */
111
+ _getMainAgent() {
112
+ console.log('[Telegram] _getMainAgent called')
113
+ console.log('[Telegram] _framework:', !!this._framework)
114
+ console.log('[Telegram] _framework._mainAgent:', !!this._framework?._mainAgent)
115
+ console.log('[Telegram] _framework._agents:', this._framework?._agents?.length || 0)
116
+
117
+ if (this._framework._mainAgent) {
118
+ console.log('[Telegram] returning _mainAgent')
119
+ return this._framework._mainAgent
120
+ }
121
+ const agents = this._framework._agents || []
122
+ console.log('[Telegram] returning last agent:', agents.length > 0 ? agents[agents.length - 1].name : 'none')
123
+ return agents.length > 0 ? agents[agents.length - 1] : null
124
+ }
125
+
126
+ /**
127
+ * 获取或创建会话Agent
128
+ */
129
+ _getSessionAgent(chatId) {
130
+ console.log('[Telegram] _getSessionAgent called for chatId:', chatId)
131
+ let session = this._sessions.get(chatId)
132
+
133
+ if (!session) {
134
+ const mainAgent = this._getMainAgent()
135
+ console.log('[Telegram] mainAgent:', !!mainAgent, mainAgent?.name)
136
+ if (!mainAgent) {
137
+ console.log('[Telegram] ERROR: mainAgent is null!')
138
+ return null
139
+ }
140
+
141
+ // 创建会话Agent(复用主Agent的配置)
142
+ session = {
143
+ agent: mainAgent,
144
+ history: [], // 对话历史
145
+ lastActive: new Date()
146
+ }
147
+ this._sessions.set(chatId, session)
148
+ console.log(`[Telegram] New session created for: ${chatId}`)
149
+ }
150
+
151
+ return session
152
+ }
153
+
154
+ /**
155
+ * 处理消息
156
+ */
157
+ async _handleMessage(msg) {
158
+ if (!msg || !msg.chat || !msg.text) return
159
+
160
+ const chatId = msg.chat.id.toString()
161
+ const text = msg.text.trim()
162
+
163
+ // 命令处理
164
+ if (text.startsWith(this.config.prefix)) {
165
+ await this._handleCommand(msg)
166
+ return
167
+ }
168
+
169
+ // 群组模式检查
170
+ if (msg.chat.type === 'group' || msg.chat.type === 'supergroup') {
171
+ if (!this.config.groupMode) return
172
+ }
173
+
174
+ // 权限检查
175
+ if (!this._checkPermission(chatId)) {
176
+ await this._sendMessage(chatId, '抱歉,您没有权限使用此 Bot。', msg.message_id)
177
+ return
178
+ }
179
+
180
+ // 处理对话
181
+ await this._processChat(chatId, text, msg.message_id)
182
+ }
183
+
184
+ /**
185
+ * 处理命令
186
+ */
187
+ async _handleCommand(msg) {
188
+ const chatId = msg.chat.id.toString()
189
+ const text = msg.text.trim()
190
+ const parts = text.split(' ')
191
+ const command = parts[0].substring(1)
192
+ const args = parts.slice(1).join(' ')
193
+
194
+ switch (command.toLowerCase()) {
195
+ case 'start':
196
+ await this._sendMessage(chatId,
197
+ '👋 欢迎使用 AI 助手!\n\n' +
198
+ '直接发送消息即可与我对话,我会尽力帮助您。\n\n' +
199
+ '可用命令:\n' +
200
+ '/start - 显示帮助\n' +
201
+ '/clear - 清除对话历史\n' +
202
+ '/history - 查看历史消息数',
203
+ msg.message_id)
204
+ break
205
+
206
+ case 'clear':
207
+ this._clearSession(chatId)
208
+ await this._sendMessage(chatId, '✅ 对话历史已清除', msg.message_id)
209
+ break
210
+
211
+ case 'history':
212
+ const session = this._sessions.get(chatId)
213
+ await this._sendMessage(chatId,
214
+ `📊 当前状态\n\n会话数:${this._sessions.size}\n` +
215
+ `历史消息:${session?.history.length || 0}\n` +
216
+ `最后活跃:${session?.lastActive?.toLocaleString() || '无'}`,
217
+ msg.message_id)
218
+ break
219
+
220
+ default:
221
+ // 未识别命令,作为普通消息处理
222
+ await this._processChat(chatId, text, msg.message_id)
223
+ }
224
+ }
225
+
226
+ /**
227
+ * 处理对话
228
+ */
229
+ async _processChat(chatId, text, replyToMessageId) {
230
+ console.log('[Telegram] _processChat called, chatId:', chatId, 'text:', text.substring(0, 50))
231
+ const session = this._getSessionAgent(chatId)
232
+ console.log('[Telegram] session:', !!session)
233
+ if (!session) {
234
+ await this._sendMessage(chatId, '❌ AI 服务未初始化', replyToMessageId)
235
+ return
236
+ }
237
+
238
+ // 添加用户消息到历史
239
+ session.history.push({ role: 'user', content: text })
240
+ session.lastActive = new Date()
241
+
242
+ // 发送思考中消息
243
+ let thinkingMsg
244
+ try {
245
+ thinkingMsg = await this._bot.sendMessage(chatId, '🤔 思考中...', {
246
+ reply_to_message_id: replyToMessageId
247
+ })
248
+ } catch (err) {
249
+ console.error('[Telegram] Send thinking error:', err.message)
250
+ return
251
+ }
252
+
253
+ try {
254
+ let fullResponse = ''
255
+ const chatIdStr = `telegram_${chatId}`
256
+
257
+ // 使用流式响应
258
+ for await (const chunk of session.agent.chatStream(text, {
259
+ sessionId: chatIdStr
260
+ })) {
261
+ // chatStream 返回 { type: 'text', text } 或 { type: 'tool-call', toolName, args }
262
+ if (chunk.type === 'text' && chunk.text) {
263
+ fullResponse += chunk.text
264
+
265
+ // 定期更新消息
266
+ if (fullResponse.length % 100 === 0) {
267
+ try {
268
+ await this._bot.editMessageText(`📝 ${fullResponse}▌`, {
269
+ chat_id: chatId,
270
+ message_id: thinkingMsg.message_id
271
+ })
272
+ } catch (e) {
273
+ // 忽略编辑错误
274
+ }
275
+ }
276
+ }
277
+ }
278
+
279
+ // 保存助手回复到历史
280
+ session.history.push({ role: 'assistant', content: fullResponse })
281
+
282
+ // 发送最终回复
283
+ await this._bot.editMessageText(fullResponse || '抱歉,我没有收到有效的回复。', {
284
+ chat_id: chatId,
285
+ message_id: thinkingMsg.message_id,
286
+ })
287
+
288
+ } catch (err) {
289
+ console.error('[Telegram] Chat error:', err)
290
+ await this._bot.editMessageText(`❌ 发生错误:${err.message}`, {
291
+ chat_id: chatId,
292
+ message_id: thinkingMsg.message_id
293
+ })
294
+ }
295
+ }
296
+
297
+ /**
298
+ * 处理编辑消息
299
+ */
300
+ async _handleEdit(msg) {
301
+ // 支持编辑后重新处理
302
+ }
303
+
304
+ /**
305
+ * 处理回调查询
306
+ */
307
+ async _handleCallback(query) {
308
+ await this._bot.answerCallbackQuery(query.id, {
309
+ text: '处理中...'
310
+ })
311
+ }
312
+
313
+ /**
314
+ * 权限检查
315
+ */
316
+ _checkPermission(chatId) {
317
+ if (this.config.allowedChats.length === 0) return true
318
+ return this.config.allowedChats.includes(chatId)
319
+ }
320
+
321
+ /**
322
+ * 清除会话
323
+ */
324
+ _clearSession(chatId) {
325
+ const session = this._sessions.get(chatId)
326
+ if (session && session.agent) {
327
+ session.agent.clearHistory?.()
328
+ }
329
+ this._sessions.delete(chatId)
330
+ }
331
+
332
+ /**
333
+ * 发送消息
334
+ */
335
+ async _sendMessage(chatId, text, replyToMessageId = null) {
336
+ if (!this._bot) return
337
+ return this._bot.sendMessage(chatId, text, {
338
+ reply_to_message_id: replyToMessageId,
339
+ })
340
+ }
341
+
342
+ /**
343
+ * 获取插件状态
344
+ */
345
+ getStatus() {
346
+ return {
347
+ connected: !!this._bot,
348
+ sessionCount: this._sessions.size,
349
+ sessions: Array.from(this._sessions.entries()).map(([chatId, session]) => ({
350
+ chatId,
351
+ historyLength: session.history.length,
352
+ lastActive: session.lastActive
353
+ })),
354
+ config: {
355
+ groupMode: this.config.groupMode,
356
+ prefix: this.config.prefix,
357
+ allowedChatsCount: this.config.allowedChats.length
358
+ }
359
+ }
360
+ }
361
+
362
+ /**
363
+ * 停止 Bot
364
+ */
365
+ stopBot() {
366
+ if (this._bot) {
367
+ this._bot.stopPolling()
368
+ this._bot = null
369
+ console.log('[Telegram] Bot stopped')
370
+ }
371
+ }
372
+
373
+ reload(framework) {
374
+ this._framework = framework
375
+ if (this.config.botToken) {
376
+ this.stopBot()
377
+ this._initBot()
378
+ }
379
+ }
380
+
381
+ uninstall(framework) {
382
+ this.stopBot()
383
+ this._sessions.clear()
384
+ this._framework = null
385
+ }
386
+ }
387
+ }
package/test-tg-bot.js ADDED
@@ -0,0 +1,42 @@
1
+ require('dotenv').config();
2
+ const { Framework } = require('./src');
3
+
4
+ async function main() {
5
+ console.log('Starting test...\n');
6
+
7
+ const framework = new Framework({ debug: false });
8
+
9
+ await framework.bootstrap({
10
+ agentDir: process.cwd() + '/.agent',
11
+ aiConfig: {
12
+ provider: process.env.FOLIKO_PROVIDER || 'minimax',
13
+ model: process.env.FOLIKO_MODEL || 'MiniMax-M2.7',
14
+ baseURL: process.env.FOLIKO_BASE_URL || 'https://api.minimaxi.com/v1',
15
+ apiKey: process.env.MINIMAX_API_KEY || process.env.DEEPSEEK_API_KEY
16
+ }
17
+ });
18
+
19
+ // 检查主 Agent
20
+ const mainAgent = framework._mainAgent;
21
+ console.log('Main Agent:', mainAgent ? mainAgent.name : 'NOT FOUND');
22
+ console.log('Agent apiKey:', mainAgent?.apiKey ? 'set' : 'NOT SET');
23
+ console.log('Agent _chatHandler:', mainAgent?._chatHandler ? 'exists' : 'NOT EXISTS');
24
+ console.log('Agent _chatHandler._aiClient:', mainAgent?._chatHandler?._aiClient ? 'exists' : 'NOT EXISTS');
25
+
26
+ // 检查 Telegram 插件
27
+ const tgPlugin = framework.pluginManager.get('telegram');
28
+ console.log('\nTelegram Plugin:', tgPlugin ? 'loaded' : 'NOT LOADED');
29
+
30
+ if (tgPlugin) {
31
+ console.log('Telegram _bot:', tgPlugin._bot ? 'exists' : 'NOT EXISTS');
32
+ }
33
+
34
+ console.log('\n========================================');
35
+ console.log('Bot is running. Send a message to test!');
36
+ console.log('========================================\n');
37
+
38
+ // 保持运行
39
+ await new Promise(() => {});
40
+ }
41
+
42
+ main().catch(console.error);