foliko 1.0.41 → 1.0.44
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/cli/src/commands/chat.js +5 -5
- package/docker-compose.yml +33 -0
- package/package.json +1 -1
- package/plugins/feishu-plugin.js +35 -2
- package/plugins/telegram-plugin.js +35 -2
- package/plugins/web-plugin.js +19 -4
- package/plugins/weixin-plugin.js +42 -2
- 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=8088
|
|
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:20-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 pip3 install uv
|
|
18
|
+
|
|
19
|
+
# 设置工作目录
|
|
20
|
+
WORKDIR /app
|
|
21
|
+
|
|
22
|
+
# 全局安装 foliko CLI
|
|
23
|
+
RUN npm install -g foliko
|
|
24
|
+
|
|
25
|
+
# 暴露端口
|
|
26
|
+
# 8088: Web 服务端口
|
|
27
|
+
EXPOSE 8088
|
|
28
|
+
|
|
29
|
+
# 环境变量(可以在 docker-compose 或运行时覆盖)
|
|
30
|
+
ENV NODE_ENV=production
|
|
31
|
+
ENV WEB_PORT=8088
|
|
32
|
+
ENV WEB_HOST=127.0.0.1
|
|
33
|
+
|
|
34
|
+
# 默认命令:运行聊天界面
|
|
35
|
+
CMD ["foliko", "chat"]
|
package/cli/src/commands/chat.js
CHANGED
|
@@ -101,11 +101,11 @@ async function chatCommand(args) {
|
|
|
101
101
|
const agent = framework.createAgent({
|
|
102
102
|
name: 'FolikoAgent',
|
|
103
103
|
systemPrompt: `你是一个有帮助的助手,擅长回答问题和执行任务。
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
104
|
+
|
|
105
|
+
**命令执行规范:**
|
|
106
|
+
- 执行 npx skills add / npx skills remove / npx skills list 命令时,必须自动添加参数:-a openclaw -y
|
|
107
|
+
- 禁止添加 -g 参数
|
|
108
|
+
- 例如:npx skills add xxx 应该执行为 npx skills add xxx -a openclaw -y`,
|
|
109
109
|
sharedPrompt: `工作目录: {{WORK_DIR}}`,
|
|
110
110
|
metadata: {
|
|
111
111
|
WORK_DIR: process.cwd() // 覆盖内置的 WORK_DIR
|
|
@@ -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
|
@@ -18,7 +18,12 @@ class FeishuPlugin extends Plugin {
|
|
|
18
18
|
this.description = '飞书对话插件,使用 WebSocket 长连接接收消息'
|
|
19
19
|
this.priority = 80
|
|
20
20
|
this.enabled = false
|
|
21
|
-
this.systemPrompt =
|
|
21
|
+
this.systemPrompt = `你是一个飞书助手。回复内容不要使用markdown格式文本。
|
|
22
|
+
|
|
23
|
+
**命令执行规范:**
|
|
24
|
+
- 执行 npx skills add / npx skills remove / npx skills list 命令时,必须自动添加参数:-a openclaw -y
|
|
25
|
+
- 禁止添加 -g 参数
|
|
26
|
+
- 例如:npx skills add xxx 应该执行为 npx skills add xxx -a openclaw -y`
|
|
22
27
|
|
|
23
28
|
this.config = {
|
|
24
29
|
appId: config.appId || process.env.FEISHU_APP_ID,
|
|
@@ -100,6 +105,11 @@ class FeishuPlugin extends Plugin {
|
|
|
100
105
|
this._framework.on('scheduler:reminder', async (data) => {
|
|
101
106
|
await this._handleScheduledReminder(data)
|
|
102
107
|
})
|
|
108
|
+
|
|
109
|
+
// 监听 webhook 事件
|
|
110
|
+
this._framework.on('webhook:received', async (data) => {
|
|
111
|
+
await this._handleWebhookNotification(data)
|
|
112
|
+
})
|
|
103
113
|
}
|
|
104
114
|
|
|
105
115
|
console.log('[Feishu] WebSocket client started')
|
|
@@ -195,7 +205,9 @@ class FeishuPlugin extends Plugin {
|
|
|
195
205
|
}
|
|
196
206
|
|
|
197
207
|
const agent = this._framework.createSessionAgent(`feishu_${openId}`, {
|
|
198
|
-
systemPrompt: this.systemPrompt
|
|
208
|
+
systemPrompt: this.systemPrompt,
|
|
209
|
+
sharedPrompt: `工作目录: {{WORK_DIR}}`,
|
|
210
|
+
metadata: { WORK_DIR: process.cwd() }
|
|
199
211
|
})
|
|
200
212
|
this._sessionAgents.set(openId, agent)
|
|
201
213
|
|
|
@@ -238,6 +250,27 @@ class FeishuPlugin extends Plugin {
|
|
|
238
250
|
}
|
|
239
251
|
}
|
|
240
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
|
+
|
|
241
274
|
async _processChat(openId, text, originalMsg) {
|
|
242
275
|
const sessionInfo = this._getSessionAgent(openId)
|
|
243
276
|
if (!sessionInfo) return
|
|
@@ -26,7 +26,12 @@ class TelegramPlugin extends Plugin {
|
|
|
26
26
|
this.description = 'Telegram 对话插件,绑定主Agent进行持续对话'
|
|
27
27
|
this.priority = 80
|
|
28
28
|
this.enabled = false
|
|
29
|
-
this.systemPrompt =
|
|
29
|
+
this.systemPrompt = `你是一个有帮助的AI助手。回复内容不要使用markdown格式文本。
|
|
30
|
+
|
|
31
|
+
**命令执行规范:**
|
|
32
|
+
- 执行 npx skills add / npx skills remove / npx skills list 命令时,必须自动添加参数:-a openclaw -y
|
|
33
|
+
- 禁止添加 -g 参数
|
|
34
|
+
- 例如:npx skills add xxx 应该执行为 npx skills add xxx -a openclaw -y`
|
|
30
35
|
|
|
31
36
|
this.config = {
|
|
32
37
|
botToken: config.botToken || process.env.TELEGRAM_BOT_TOKEN,
|
|
@@ -97,6 +102,11 @@ class TelegramPlugin extends Plugin {
|
|
|
97
102
|
this._framework.on('scheduler:reminder', async (data) => {
|
|
98
103
|
await this._handleScheduledReminder(data)
|
|
99
104
|
})
|
|
105
|
+
|
|
106
|
+
// 监听 webhook 事件
|
|
107
|
+
this._framework.on('webhook:received', async (data) => {
|
|
108
|
+
await this._handleWebhookNotification(data)
|
|
109
|
+
})
|
|
100
110
|
}
|
|
101
111
|
} catch (err) {
|
|
102
112
|
console.error('[Telegram] Failed to initialize bot:', err.message)
|
|
@@ -135,6 +145,27 @@ class TelegramPlugin extends Plugin {
|
|
|
135
145
|
}
|
|
136
146
|
}
|
|
137
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
|
+
|
|
138
169
|
_getMainAgent() {
|
|
139
170
|
if (this._framework._mainAgent) return this._framework._mainAgent
|
|
140
171
|
const agents = this._framework._agents || []
|
|
@@ -147,7 +178,9 @@ class TelegramPlugin extends Plugin {
|
|
|
147
178
|
}
|
|
148
179
|
|
|
149
180
|
const agent = this._framework.createSessionAgent(`telegram_${chatId}`, {
|
|
150
|
-
systemPrompt: this.systemPrompt
|
|
181
|
+
systemPrompt: this.systemPrompt,
|
|
182
|
+
sharedPrompt: `工作目录: {{WORK_DIR}}`,
|
|
183
|
+
metadata: { WORK_DIR: process.cwd() }
|
|
151
184
|
})
|
|
152
185
|
this._sessionAgents.set(chatId, agent)
|
|
153
186
|
|
package/plugins/web-plugin.js
CHANGED
|
@@ -202,7 +202,22 @@ class WebPlugin extends Plugin {
|
|
|
202
202
|
|
|
203
203
|
// 1. 静态文件
|
|
204
204
|
const staticResult = this._serveStatic(pathname)
|
|
205
|
-
if (staticResult)
|
|
205
|
+
if (staticResult) {
|
|
206
|
+
if (staticResult.type === 'file') {
|
|
207
|
+
return c.newResponse(staticResult.content, {
|
|
208
|
+
headers: { 'Content-Type': staticResult.contentType }
|
|
209
|
+
})
|
|
210
|
+
}
|
|
211
|
+
if (staticResult.type === 'notFound') {
|
|
212
|
+
return c.json({ error: 'Not Found' }, 404)
|
|
213
|
+
}
|
|
214
|
+
if (staticResult.type === 'forbidden') {
|
|
215
|
+
return c.json({ error: 'Forbidden' }, 403)
|
|
216
|
+
}
|
|
217
|
+
if (staticResult.type === 'error') {
|
|
218
|
+
return c.json({ error: staticResult.message }, 500)
|
|
219
|
+
}
|
|
220
|
+
}
|
|
206
221
|
|
|
207
222
|
// 2. Webhook(精确匹配)
|
|
208
223
|
const webhook = this._webhooks.get(pathname)
|
|
@@ -261,7 +276,7 @@ class WebPlugin extends Plugin {
|
|
|
261
276
|
const finalSessionId = sessionId || `web_${Date.now()}`
|
|
262
277
|
|
|
263
278
|
// 触发 webhook 接收事件
|
|
264
|
-
this._framework.emit('webhook:received', { webhook, data: webhookData })
|
|
279
|
+
this._framework.emit('webhook:received', { webhook, data: webhookData, sessionId: finalSessionId })
|
|
265
280
|
|
|
266
281
|
if (!webhook.awaitResponse) {
|
|
267
282
|
// 不等待,立即返回
|
|
@@ -281,7 +296,7 @@ class WebPlugin extends Plugin {
|
|
|
281
296
|
}
|
|
282
297
|
|
|
283
298
|
// 触发 webhook 处理完成事件
|
|
284
|
-
this._framework.emit('webhook:received', { webhook, data: webhookData, response: responseText })
|
|
299
|
+
this._framework.emit('webhook:received', { webhook, data: webhookData, response: responseText, sessionId: finalSessionId })
|
|
285
300
|
}).catch(err => {
|
|
286
301
|
console.error('[Web] Webhook error:', err.message)
|
|
287
302
|
})
|
|
@@ -307,7 +322,7 @@ class WebPlugin extends Plugin {
|
|
|
307
322
|
}
|
|
308
323
|
|
|
309
324
|
// 触发 webhook 处理完成事件
|
|
310
|
-
this._framework.emit('webhook:received', { webhook, data: webhookData, response: responseText })
|
|
325
|
+
this._framework.emit('webhook:received', { webhook, data: webhookData, response: responseText, sessionId: finalSessionId })
|
|
311
326
|
|
|
312
327
|
return { success: true, message: 'Webhook processed', response: responseText }
|
|
313
328
|
} catch (err) {
|
package/plugins/weixin-plugin.js
CHANGED
|
@@ -21,7 +21,12 @@ class WeixinPlugin extends Plugin {
|
|
|
21
21
|
// 默认不启用,需要在 plugins.json 中设置 enabled: true
|
|
22
22
|
this.enabled = false
|
|
23
23
|
this.path=`.agent/data`
|
|
24
|
-
this.systemPrompt
|
|
24
|
+
this.systemPrompt=`你是一个微信助手。回复内容不要使用markdown格式文本
|
|
25
|
+
|
|
26
|
+
**命令执行规范:**
|
|
27
|
+
- 执行 npx skills add / npx skills remove / npx skills list 命令时,必须自动添加参数:-a openclaw -y
|
|
28
|
+
- 禁止添加 -g 参数
|
|
29
|
+
- 例如:npx skills add xxx 应该执行为 npx skills add xxx -a openclaw -y`
|
|
25
30
|
|
|
26
31
|
this.config = {
|
|
27
32
|
forceLogin: config.forceLogin || process.env.WEIXIN_FORCE_LOGIN === 'true',
|
|
@@ -69,6 +74,12 @@ class WeixinPlugin extends Plugin {
|
|
|
69
74
|
console.log('[WeChat] Received scheduler reminder:', data)
|
|
70
75
|
await this._handleScheduledReminder(data)
|
|
71
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
|
+
})
|
|
72
83
|
}
|
|
73
84
|
|
|
74
85
|
// 异步初始化 Bot
|
|
@@ -160,7 +171,9 @@ class WeixinPlugin extends Plugin {
|
|
|
160
171
|
|
|
161
172
|
// 创建新 agent
|
|
162
173
|
const agent = this._framework.createSessionAgent(`weixin_${userId}`, {
|
|
163
|
-
systemPrompt: this.systemPrompt
|
|
174
|
+
systemPrompt: this.systemPrompt,
|
|
175
|
+
sharedPrompt: `工作目录: {{WORK_DIR}}`,
|
|
176
|
+
metadata: { WORK_DIR: process.cwd() }
|
|
164
177
|
})
|
|
165
178
|
this._sessionAgents.set(userId, agent)
|
|
166
179
|
console.log('[WeChat] Created new session agent for userId:', userId)
|
|
@@ -327,6 +340,33 @@ class WeixinPlugin extends Plugin {
|
|
|
327
340
|
}
|
|
328
341
|
}
|
|
329
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
|
+
|
|
330
370
|
/**
|
|
331
371
|
* 获取插件状态
|
|
332
372
|
*/
|
package/test.txt
ADDED