foliko 1.0.43 → 1.0.46
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/.dockerignore +45 -0
- package/.env.example +10 -0
- package/Dockerfile +35 -0
- package/docker-compose.yml +33 -0
- package/package.json +1 -1
- package/plugins/feishu-plugin.js +26 -0
- package/plugins/telegram-plugin.js +26 -0
- package/plugins/web-plugin.js +29 -12
- package/plugins/weixin-plugin.js +33 -0
- package/test.txt +3 -0
package/.dockerignore
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# 依赖
|
|
2
|
+
node_modules
|
|
3
|
+
.agent/node_modules
|
|
4
|
+
|
|
5
|
+
# 日志
|
|
6
|
+
*.log
|
|
7
|
+
npm-debug.log*
|
|
8
|
+
|
|
9
|
+
# 环境变量(包含敏感信息)
|
|
10
|
+
.env
|
|
11
|
+
.env.local
|
|
12
|
+
|
|
13
|
+
# Git
|
|
14
|
+
.git
|
|
15
|
+
.gitignore
|
|
16
|
+
|
|
17
|
+
# IDE
|
|
18
|
+
.idea
|
|
19
|
+
.vscode
|
|
20
|
+
*.swp
|
|
21
|
+
*.swo
|
|
22
|
+
|
|
23
|
+
# 文档(可选保留)
|
|
24
|
+
*.md
|
|
25
|
+
!README.md
|
|
26
|
+
|
|
27
|
+
# 测试
|
|
28
|
+
test
|
|
29
|
+
tests
|
|
30
|
+
coverage
|
|
31
|
+
|
|
32
|
+
# 临时文件
|
|
33
|
+
tmp
|
|
34
|
+
temp
|
|
35
|
+
*.tmp
|
|
36
|
+
|
|
37
|
+
# Docker 相关(避免递归)
|
|
38
|
+
Dockerfile*
|
|
39
|
+
docker-compose*
|
|
40
|
+
.dockerignore
|
|
41
|
+
|
|
42
|
+
# CI/CD
|
|
43
|
+
.github
|
|
44
|
+
.gitlab-ci.yml
|
|
45
|
+
.travis.yml
|
package/.env.example
CHANGED
|
@@ -38,3 +38,13 @@ FROM_EMAIL=your-email@gmail.com
|
|
|
38
38
|
|
|
39
39
|
# ========== Telegram Bot (optional) ==========
|
|
40
40
|
TELEGRAM_BOT_TOKEN=your-telegram-bot-token
|
|
41
|
+
|
|
42
|
+
# ========== Web Server (optional) ==========
|
|
43
|
+
# Web 服务端口,默认 8088
|
|
44
|
+
WEB_PORT=3000
|
|
45
|
+
|
|
46
|
+
# Web 服务主机,默认 127.0.0.1
|
|
47
|
+
WEB_HOST=127.0.0.1
|
|
48
|
+
|
|
49
|
+
# 公网访问的 base URL(用于生成 webhook URL 等),不设置则使用 host:port
|
|
50
|
+
WEB_BASE_URL=https://your-domain.com
|
package/Dockerfile
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# ========== Foliko - 插件化 Agent 框架 ==========
|
|
2
|
+
# 基于 Node.js 20 LTS
|
|
3
|
+
|
|
4
|
+
FROM node:25-slim
|
|
5
|
+
|
|
6
|
+
# 安装 Python 和构建工具(部分 npm 包需要编译)
|
|
7
|
+
RUN apt-get update && apt-get install -y --no-install-recommends \
|
|
8
|
+
build-essential \
|
|
9
|
+
python3 \
|
|
10
|
+
python3-pip \
|
|
11
|
+
curl \
|
|
12
|
+
git \
|
|
13
|
+
&& rm -rf /var/lib/apt/lists/* \
|
|
14
|
+
&& ln -sf /usr/bin/python3 /usr/bin/python
|
|
15
|
+
|
|
16
|
+
# 安装 uv(快速的 Python 包管理器)
|
|
17
|
+
RUN curl -LsSf https://astral.sh/uv/install.sh | sh
|
|
18
|
+
ENV PATH="/root/.local/bin:$PATH"
|
|
19
|
+
|
|
20
|
+
# 设置工作目录
|
|
21
|
+
WORKDIR /app
|
|
22
|
+
|
|
23
|
+
# 全局安装 foliko CLI
|
|
24
|
+
RUN npm install -g foliko
|
|
25
|
+
|
|
26
|
+
# 暴露端口
|
|
27
|
+
# 8088: Web 服务端口
|
|
28
|
+
EXPOSE 3000
|
|
29
|
+
|
|
30
|
+
# 环境变量(可以在 docker-compose 或运行时覆盖)
|
|
31
|
+
ENV NODE_ENV=production
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
# 默认命令:运行聊天界面
|
|
35
|
+
CMD ["foliko", "chat"]
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# ========== Foliko Docker Compose ==========
|
|
2
|
+
# 使用方式: docker-compose up -d
|
|
3
|
+
|
|
4
|
+
services:
|
|
5
|
+
foliko:
|
|
6
|
+
build: .
|
|
7
|
+
container_name: foliko-agent
|
|
8
|
+
restart: unless-stopped
|
|
9
|
+
ports:
|
|
10
|
+
- "8088:8088"
|
|
11
|
+
environment:
|
|
12
|
+
# AI 配置
|
|
13
|
+
- FOLIKO_PROVIDER=${FOLIKO_PROVIDER:-minimax}
|
|
14
|
+
- FOLIKO_MODEL=${FOLIKO_MODEL:-}
|
|
15
|
+
- FOLIKO_BASE_URL=${FOLIKO_BASE_URL:-}
|
|
16
|
+
- FOLIKO_API_KEY=${FOLIKO_API_KEY:-}
|
|
17
|
+
# Web 服务配置
|
|
18
|
+
- WEB_PORT=8088
|
|
19
|
+
- WEB_HOST=0.0.0.0
|
|
20
|
+
- WEB_BASE_URL=${WEB_BASE_URL:-}
|
|
21
|
+
# Telegram 配置(可选)
|
|
22
|
+
- TELEGRAM_BOT_TOKEN=${TELEGRAM_BOT_TOKEN:-}
|
|
23
|
+
# 飞书配置(可选)
|
|
24
|
+
- FEISHU_APP_ID=${FEISHU_APP_ID:-}
|
|
25
|
+
- FEISHU_APP_SECRET=${FEISHU_APP_SECRET:-}
|
|
26
|
+
volumes:
|
|
27
|
+
# 持久化数据
|
|
28
|
+
- foliko-data:/app/.agent/data
|
|
29
|
+
tty: true
|
|
30
|
+
stdin_open: true
|
|
31
|
+
|
|
32
|
+
volumes:
|
|
33
|
+
foliko-data:
|
package/package.json
CHANGED
package/plugins/feishu-plugin.js
CHANGED
|
@@ -105,6 +105,11 @@ class FeishuPlugin extends Plugin {
|
|
|
105
105
|
this._framework.on('scheduler:reminder', async (data) => {
|
|
106
106
|
await this._handleScheduledReminder(data)
|
|
107
107
|
})
|
|
108
|
+
|
|
109
|
+
// 监听 webhook 事件
|
|
110
|
+
this._framework.on('webhook:received', async (data) => {
|
|
111
|
+
await this._handleWebhookNotification(data)
|
|
112
|
+
})
|
|
108
113
|
}
|
|
109
114
|
|
|
110
115
|
console.log('[Feishu] WebSocket client started')
|
|
@@ -245,6 +250,27 @@ class FeishuPlugin extends Plugin {
|
|
|
245
250
|
}
|
|
246
251
|
}
|
|
247
252
|
|
|
253
|
+
/**
|
|
254
|
+
* 处理 webhook 通知
|
|
255
|
+
*/
|
|
256
|
+
async _handleWebhookNotification(data) {
|
|
257
|
+
const { data: webhookData, response, sessionId } = data
|
|
258
|
+
|
|
259
|
+
if (!sessionId || !sessionId.startsWith('feishu_')) {
|
|
260
|
+
return
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
const openId = sessionId.replace('feishu_', '')
|
|
264
|
+
const notificationText = `📥 [Webhook 接收]\n\n路径: ${webhookData.path}\n方法: ${webhookData.method}\n\n处理结果: ${response || '处理中...'}`
|
|
265
|
+
|
|
266
|
+
try {
|
|
267
|
+
await this._sendTextMessage(openId, notificationText)
|
|
268
|
+
console.log(`[Feishu] Webhook notification sent to ${openId}`)
|
|
269
|
+
} catch (err) {
|
|
270
|
+
console.error(`[Feishu] Failed to send webhook notification:`, err.message)
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
248
274
|
async _processChat(openId, text, originalMsg) {
|
|
249
275
|
const sessionInfo = this._getSessionAgent(openId)
|
|
250
276
|
if (!sessionInfo) return
|
|
@@ -102,6 +102,11 @@ class TelegramPlugin extends Plugin {
|
|
|
102
102
|
this._framework.on('scheduler:reminder', async (data) => {
|
|
103
103
|
await this._handleScheduledReminder(data)
|
|
104
104
|
})
|
|
105
|
+
|
|
106
|
+
// 监听 webhook 事件
|
|
107
|
+
this._framework.on('webhook:received', async (data) => {
|
|
108
|
+
await this._handleWebhookNotification(data)
|
|
109
|
+
})
|
|
105
110
|
}
|
|
106
111
|
} catch (err) {
|
|
107
112
|
console.error('[Telegram] Failed to initialize bot:', err.message)
|
|
@@ -140,6 +145,27 @@ class TelegramPlugin extends Plugin {
|
|
|
140
145
|
}
|
|
141
146
|
}
|
|
142
147
|
|
|
148
|
+
/**
|
|
149
|
+
* 处理 webhook 通知
|
|
150
|
+
*/
|
|
151
|
+
async _handleWebhookNotification(data) {
|
|
152
|
+
const { data: webhookData, response, sessionId } = data
|
|
153
|
+
|
|
154
|
+
if (!sessionId || !sessionId.startsWith('telegram_')) {
|
|
155
|
+
return
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const chatId = sessionId.replace('telegram_', '')
|
|
159
|
+
const notificationText = `📥 [Webhook 接收]\n\n路径: ${webhookData.path}\n方法: ${webhookData.method}\n\n处理结果: ${response || '处理中...'}`
|
|
160
|
+
|
|
161
|
+
try {
|
|
162
|
+
await this._bot.sendMessage(chatId, notificationText)
|
|
163
|
+
console.log(`[Telegram] Webhook notification sent to chat ${chatId}`)
|
|
164
|
+
} catch (err) {
|
|
165
|
+
console.error(`[Telegram] Failed to send webhook notification:`, err.message)
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
143
169
|
_getMainAgent() {
|
|
144
170
|
if (this._framework._mainAgent) return this._framework._mainAgent
|
|
145
171
|
const agents = this._framework._agents || []
|
package/plugins/web-plugin.js
CHANGED
|
@@ -19,9 +19,9 @@ class WebPlugin extends Plugin {
|
|
|
19
19
|
this.priority = 50
|
|
20
20
|
|
|
21
21
|
// 服务器配置
|
|
22
|
-
this._port =
|
|
23
|
-
this._host =
|
|
24
|
-
this._baseUrl =
|
|
22
|
+
this._port = process.env.WEB_PORT || 3000
|
|
23
|
+
this._host = process.env.WEB_HOST || '127.0.0.1'
|
|
24
|
+
this._baseUrl = process.env.WEB_BASE_URL || null // 公网可访问的域名
|
|
25
25
|
|
|
26
26
|
// 运行时状态
|
|
27
27
|
this._server = null
|
|
@@ -157,12 +157,14 @@ class WebPlugin extends Plugin {
|
|
|
157
157
|
port: this._port,
|
|
158
158
|
hostname: this._host
|
|
159
159
|
})
|
|
160
|
-
|
|
160
|
+
const serverUrl = this._getUrl()
|
|
161
|
+
console.log(`[Web] Server started on ${serverUrl}`)
|
|
161
162
|
return {
|
|
162
163
|
success: true,
|
|
163
|
-
message: `Server started on
|
|
164
|
+
message: `Server started on ${serverUrl}`,
|
|
164
165
|
port: this._port,
|
|
165
|
-
host: this._host
|
|
166
|
+
host: this._host,
|
|
167
|
+
url: serverUrl
|
|
166
168
|
}
|
|
167
169
|
} catch (err) {
|
|
168
170
|
this._server = null
|
|
@@ -202,7 +204,22 @@ class WebPlugin extends Plugin {
|
|
|
202
204
|
|
|
203
205
|
// 1. 静态文件
|
|
204
206
|
const staticResult = this._serveStatic(pathname)
|
|
205
|
-
if (staticResult)
|
|
207
|
+
if (staticResult) {
|
|
208
|
+
if (staticResult.type === 'file') {
|
|
209
|
+
return c.newResponse(staticResult.content, {
|
|
210
|
+
headers: { 'Content-Type': staticResult.contentType }
|
|
211
|
+
})
|
|
212
|
+
}
|
|
213
|
+
if (staticResult.type === 'notFound') {
|
|
214
|
+
return c.json({ error: 'Not Found' }, 404)
|
|
215
|
+
}
|
|
216
|
+
if (staticResult.type === 'forbidden') {
|
|
217
|
+
return c.json({ error: 'Forbidden' }, 403)
|
|
218
|
+
}
|
|
219
|
+
if (staticResult.type === 'error') {
|
|
220
|
+
return c.json({ error: staticResult.message }, 500)
|
|
221
|
+
}
|
|
222
|
+
}
|
|
206
223
|
|
|
207
224
|
// 2. Webhook(精确匹配)
|
|
208
225
|
const webhook = this._webhooks.get(pathname)
|
|
@@ -261,7 +278,7 @@ class WebPlugin extends Plugin {
|
|
|
261
278
|
const finalSessionId = sessionId || `web_${Date.now()}`
|
|
262
279
|
|
|
263
280
|
// 触发 webhook 接收事件
|
|
264
|
-
this._framework.emit('webhook:received', { webhook, data: webhookData })
|
|
281
|
+
this._framework.emit('webhook:received', { webhook, data: webhookData, sessionId: finalSessionId })
|
|
265
282
|
|
|
266
283
|
if (!webhook.awaitResponse) {
|
|
267
284
|
// 不等待,立即返回
|
|
@@ -281,7 +298,7 @@ class WebPlugin extends Plugin {
|
|
|
281
298
|
}
|
|
282
299
|
|
|
283
300
|
// 触发 webhook 处理完成事件
|
|
284
|
-
this._framework.emit('webhook:received', { webhook, data: webhookData, response: responseText })
|
|
301
|
+
this._framework.emit('webhook:received', { webhook, data: webhookData, response: responseText, sessionId: finalSessionId })
|
|
285
302
|
}).catch(err => {
|
|
286
303
|
console.error('[Web] Webhook error:', err.message)
|
|
287
304
|
})
|
|
@@ -307,7 +324,7 @@ class WebPlugin extends Plugin {
|
|
|
307
324
|
}
|
|
308
325
|
|
|
309
326
|
// 触发 webhook 处理完成事件
|
|
310
|
-
this._framework.emit('webhook:received', { webhook, data: webhookData, response: responseText })
|
|
327
|
+
this._framework.emit('webhook:received', { webhook, data: webhookData, response: responseText, sessionId: finalSessionId })
|
|
311
328
|
|
|
312
329
|
return { success: true, message: 'Webhook processed', response: responseText }
|
|
313
330
|
} catch (err) {
|
|
@@ -391,7 +408,7 @@ class WebPlugin extends Plugin {
|
|
|
391
408
|
}
|
|
392
409
|
|
|
393
410
|
_listRoutes() {
|
|
394
|
-
const baseUrl = this._getUrl(
|
|
411
|
+
const baseUrl = this._getUrl()
|
|
395
412
|
|
|
396
413
|
return {
|
|
397
414
|
success: true,
|
|
@@ -462,7 +479,7 @@ class WebPlugin extends Plugin {
|
|
|
462
479
|
return query
|
|
463
480
|
}
|
|
464
481
|
|
|
465
|
-
_getUrl(path) {
|
|
482
|
+
_getUrl(path='') {
|
|
466
483
|
if (this._baseUrl) {
|
|
467
484
|
return `${this._baseUrl}${path}`
|
|
468
485
|
}
|
package/plugins/weixin-plugin.js
CHANGED
|
@@ -74,6 +74,12 @@ class WeixinPlugin extends Plugin {
|
|
|
74
74
|
console.log('[WeChat] Received scheduler reminder:', data)
|
|
75
75
|
await this._handleScheduledReminder(data)
|
|
76
76
|
})
|
|
77
|
+
|
|
78
|
+
// 监听 webhook 事件
|
|
79
|
+
this._framework.on('webhook:received', async (data) => {
|
|
80
|
+
console.log('[WeChat] Received webhook event:', data)
|
|
81
|
+
await this._handleWebhookNotification(data)
|
|
82
|
+
})
|
|
77
83
|
}
|
|
78
84
|
|
|
79
85
|
// 异步初始化 Bot
|
|
@@ -334,6 +340,33 @@ class WeixinPlugin extends Plugin {
|
|
|
334
340
|
}
|
|
335
341
|
}
|
|
336
342
|
|
|
343
|
+
/**
|
|
344
|
+
* 处理 webhook 通知
|
|
345
|
+
*/
|
|
346
|
+
async _handleWebhookNotification(data) {
|
|
347
|
+
const { data: webhookData, response, sessionId } = data
|
|
348
|
+
|
|
349
|
+
if (!this._bot) {
|
|
350
|
+
console.warn('[WeChat] Bot not ready, cannot send webhook notification')
|
|
351
|
+
return
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
// 只处理 weixin 相关的 session
|
|
355
|
+
if (!sessionId || !sessionId.startsWith('weixin_')) {
|
|
356
|
+
return
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
const userId = sessionId.replace('weixin_', '')
|
|
360
|
+
const notificationText = `📥 [Webhook 接收]\n\n路径: ${webhookData.path}\n方法: ${webhookData.method}\n\n处理结果: ${response || '处理中...'}`
|
|
361
|
+
|
|
362
|
+
try {
|
|
363
|
+
await this._bot.sendText(userId, notificationText)
|
|
364
|
+
console.log(`[WeChat] Webhook notification sent to user ${userId}`)
|
|
365
|
+
} catch (err) {
|
|
366
|
+
console.error(`[WeChat] Failed to send webhook notification:`, err.message)
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
|
|
337
370
|
/**
|
|
338
371
|
* 获取插件状态
|
|
339
372
|
*/
|
package/test.txt
ADDED