foliko 1.1.68 → 1.1.69
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/.claude/settings.local.json +19 -10
- package/.dockerignore +45 -45
- package/.env.example +56 -56
- package/CLAUDE.md +2 -2
- package/README.md +13 -13
- package/SPEC.md +3 -3
- package/cli/src/commands/chat.js +2 -20
- package/cli/src/commands/list.js +7 -6
- package/cli/src/commands/plugin.js +3 -2
- package/cli/src/daemon.js +2 -2
- package/cli/src/ui/chat-ui-old.js +15 -4
- package/cli/src/ui/chat-ui.js +152 -38
- package/cli/src/ui/footer-bar.js +13 -14
- package/cli/src/utils/config.js +29 -0
- package/cli/src/utils/plugin-config.js +1 -1
- package/docker-compose.yml +33 -33
- package/docs/features.md +120 -120
- package/docs/quick-reference.md +160 -160
- package/docs/user-manual.md +1391 -1391
- package/examples/ambient-example.js +2 -2
- package/examples/bootstrap.js +3 -3
- package/examples/test-chat.js +1 -1
- package/examples/test-reload.js +1 -1
- package/examples/test-telegram.js +1 -1
- package/examples/test-tg-bot.js +1 -1
- package/examples/test-tg-simple.js +2 -2
- package/examples/test-tg.js +1 -1
- package/examples/test-think.js +1 -1
- package/examples/test-weixin-feishu.js +3 -3
- package/package.json +3 -1
- package/plugins/ambient-agent/index.js +1 -1
- package/plugins/audit-plugin.js +75 -24
- package/plugins/default-plugins.js +21 -11
- package/plugins/email/index.js +1 -1
- package/plugins/extension-executor-plugin.js +85 -7
- package/plugins/feishu-plugin.js +118 -16
- package/plugins/install-plugin.js +4 -4
- package/plugins/memory-plugin.js +1 -1
- package/plugins/plugin-manager-plugin.js +3 -3
- package/plugins/python-executor-plugin.js +1 -1
- package/plugins/python-plugin-loader.js +1 -1
- package/plugins/qq-plugin.js +143 -16
- package/plugins/rules-plugin.js +1 -1
- package/plugins/scheduler-plugin.js +2 -2
- package/plugins/session-plugin.js +309 -393
- package/plugins/storage-plugin.js +235 -176
- package/plugins/subagent-plugin.js +4 -4
- package/plugins/telegram-plugin.js +116 -17
- package/plugins/think-plugin.js +1 -1
- package/plugins/web-plugin.js +2 -2
- package/plugins/weixin-plugin.js +102 -19
- package/skills/find-skills/AGENTS.md +2 -2
- package/skills/find-skills/SKILL.md +133 -133
- package/skills/foliko-dev/AGENTS.md +236 -236
- package/skills/foliko-dev/SKILL.md +19 -19
- package/skills/mcp-usage/SKILL.md +200 -200
- package/skills/plugin-guide/SKILL.md +4 -4
- package/skills/python-plugin-dev/SKILL.md +5 -5
- package/skills/skill-guide/SKILL.md +104 -6
- package/skills/subagent-guide/SKILL.md +237 -237
- package/skills/workflow-guide/SKILL.md +646 -646
- package/src/capabilities/skill-manager.js +101 -2
- package/src/capabilities/workflow-engine.js +1 -1
- package/src/core/agent-chat.js +17 -14
- package/src/core/branch-summary-auto.js +206 -0
- package/src/core/chat-session.js +38 -8
- package/src/core/command-registry.js +200 -0
- package/src/core/constants.js +11 -11
- package/src/core/context-compressor.js +700 -149
- package/src/core/context-manager.js +0 -1
- package/src/core/enhanced-context-compressor.js +210 -0
- package/src/core/framework.js +140 -83
- package/src/core/jsonl-storage.js +253 -0
- package/src/core/plugin-manager.js +15 -10
- package/src/core/provider-registry.js +159 -0
- package/src/core/provider.js +2 -0
- package/src/core/session-entry.js +225 -0
- package/src/core/session-manager.js +701 -0
- package/src/core/storage-manager.js +494 -0
- package/src/core/sub-agent-config.js +1 -1
- package/src/core/token-counter.js +177 -58
- package/src/core/ui-extension-context.js +174 -0
- package/src/executors/mcp-executor.js +15 -6
- package/src/utils/logger.js +152 -180
- package/src/utils/plugin-helpers.js +2 -2
- package/website_v2/docs/api.html +1 -1
- package/website_v2/docs/configuration.html +2 -2
- package/website_v2/docs/plugin-development.html +4 -4
- package/website_v2/docs/project-structure.html +2 -2
- package/website_v2/docs/skill-development.html +2 -2
- package/website_v2/index.html +1 -1
- package/website_v2/styles/animations.css +7 -7
- package/.agent/agents/backend-dev.md +0 -102
- package/.agent/agents/data-analyst.md +0 -117
- package/.agent/agents/devops.md +0 -115
- package/.agent/agents/frontend-dev.md +0 -94
- package/.agent/agents/network-requester.md +0 -44
- package/.agent/agents/poster-designer.md +0 -52
- package/.agent/agents/product-manager.md +0 -85
- package/.agent/agents/qa-engineer.md +0 -100
- package/.agent/agents/security-engineer.md +0 -99
- package/.agent/agents/team-lead.md +0 -137
- package/.agent/agents/ui-designer.md +0 -116
- package/.agent/data/default.json +0 -58
- package/.agent/data/email/processed-emails.json +0 -1
- package/.agent/data/plugins-state.json +0 -207
- package/.agent/data/scheduler/tasks.json +0 -1
- package/.agent/data/web/web-config.json +0 -5
- package/.agent/data/weixin/images/file_1776188148383jpg +0 -0
- package/.agent/data/weixin/images/file_1776188458326.jpg +0 -0
- package/.agent/data/weixin/images/file_1776188689423.jpg +0 -0
- package/.agent/data/weixin/images/file_1776188813604.jpg +0 -0
- package/.agent/data/weixin/images/file_1776189097450.jpg +0 -0
- package/.agent/data/weixin/videos/file_1776188318431.mp4 +0 -0
- package/.agent/data/weixin.json +0 -6
- package/.agent/mcp_config.json +0 -14
- package/.agent/memory/user/mof6gk94-kneeuh.md +0 -9
- package/.agent/package.json +0 -8
- package/.agent/plugins/marknative/README.md +0 -134
- package/.agent/plugins/marknative/fonts/SegoeUI Emoji.ttf +0 -0
- package/.agent/plugins/marknative/fonts.zip +0 -0
- package/.agent/plugins/marknative/index.js +0 -256
- package/.agent/plugins/marknative/package.json +0 -12
- package/.agent/plugins/test-plugin.py +0 -99
- package/.agent/plugins.json +0 -14
- package/.agent/python-scripts/test_sample.py +0 -24
- package/.agent/sessions/cli_default.json +0 -4121
- package/.agent/skills/agent-browser/SKILL.md +0 -311
- package/.agent/skills/agent-browser/TEST_PLAN.md +0 -200
- package/.agent/skills/sysinfo/SKILL.md +0 -38
- package/.agent/skills/sysinfo/system-info.sh +0 -130
- package/.agent/skills/workflow/SKILL.md +0 -324
- package/.agent/test-agent.js +0 -35
- package/.agent/weixin.json +0 -6
- package/.agent/workflows/email-digest.json +0 -50
- package/.agent/workflows/file-backup.json +0 -21
- package/.agent/workflows/get-ip-notify.json +0 -32
- package/.agent/workflows/news-aggregator.json +0 -93
- package/.agent/workflows/news-dashboard-v2.json +0 -94
- package/.agent/workflows/notification-batch.json +0 -32
- package/src/core/session-context.js +0 -346
- package/src/core/session-storage.js +0 -295
|
@@ -15,7 +15,7 @@ async function main() {
|
|
|
15
15
|
|
|
16
16
|
// Bootstrap with default plugins (including ambient)
|
|
17
17
|
await framework.bootstrap({
|
|
18
|
-
agentDir: './.
|
|
18
|
+
agentDir: './.foliko',
|
|
19
19
|
aiConfig: {
|
|
20
20
|
provider: 'deepseek',
|
|
21
21
|
model: 'deepseek-chat',
|
|
@@ -181,7 +181,7 @@ async function main() {
|
|
|
181
181
|
}
|
|
182
182
|
|
|
183
183
|
console.log('\n=== All Examples Completed ===');
|
|
184
|
-
console.log('Check .
|
|
184
|
+
console.log('Check .foliko/data/ambient/ for persisted data');
|
|
185
185
|
|
|
186
186
|
// Cleanup
|
|
187
187
|
console.log('\nShutting down...');
|
package/examples/bootstrap.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Bootstrap 示例
|
|
3
|
-
* 使用 framework.bootstrap() 自动加载 .
|
|
3
|
+
* 使用 framework.bootstrap() 自动加载 .foliko/ 目录配置
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
const { Framework } = require('../src');
|
|
@@ -13,9 +13,9 @@ async function main() {
|
|
|
13
13
|
const framework = new Framework({ debug: true });
|
|
14
14
|
|
|
15
15
|
// 使用 bootstrap 自动加载所有默认插件
|
|
16
|
-
// 会自动检测 .
|
|
16
|
+
// 会自动检测 .foliko/ 目录下的配置
|
|
17
17
|
await framework.bootstrap({
|
|
18
|
-
agentDir: './.
|
|
18
|
+
agentDir: './.foliko', // 配置目录
|
|
19
19
|
aiConfig: {
|
|
20
20
|
// 可选:覆盖 AI 配置
|
|
21
21
|
provider: 'deepseek',
|
package/examples/test-chat.js
CHANGED
package/examples/test-reload.js
CHANGED
|
@@ -37,7 +37,7 @@ async function test() {
|
|
|
37
37
|
|
|
38
38
|
// 检查配置
|
|
39
39
|
console.log('\n3. 当前配置文件:');
|
|
40
|
-
const configPath = path.resolve('.
|
|
40
|
+
const configPath = path.resolve('.foliko/mcp_config.json');
|
|
41
41
|
if (fs.existsSync(configPath)) {
|
|
42
42
|
const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
43
43
|
console.log(JSON.stringify(config, null, 2));
|
|
@@ -5,7 +5,7 @@ async function main() {
|
|
|
5
5
|
const framework = new Framework({ debug: false });
|
|
6
6
|
|
|
7
7
|
await framework.bootstrap({
|
|
8
|
-
agentDir: process.cwd() + '/.
|
|
8
|
+
agentDir: process.cwd() + '/.foliko',
|
|
9
9
|
aiConfig: {
|
|
10
10
|
provider: process.env.FOLIKO_PROVIDER || 'minimax',
|
|
11
11
|
model: process.env.FOLIKO_MODEL || 'MiniMax-M2.7',
|
package/examples/test-tg-bot.js
CHANGED
|
@@ -7,7 +7,7 @@ async function main() {
|
|
|
7
7
|
const framework = new Framework({ debug: false });
|
|
8
8
|
|
|
9
9
|
await framework.bootstrap({
|
|
10
|
-
agentDir: process.cwd() + '/.
|
|
10
|
+
agentDir: process.cwd() + '/.foliko',
|
|
11
11
|
aiConfig: {
|
|
12
12
|
provider: process.env.FOLIKO_PROVIDER || 'minimax',
|
|
13
13
|
model: process.env.FOLIKO_MODEL || 'MiniMax-M2.7',
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
require('dotenv').config();
|
|
2
2
|
|
|
3
|
-
// 添加 .
|
|
3
|
+
// 添加 .foliko/node_modules 到搜索路径
|
|
4
4
|
const path = require('path');
|
|
5
|
-
const agentNodeModules = path.join(process.cwd(), '.
|
|
5
|
+
const agentNodeModules = path.join(process.cwd(), '.foliko', 'node_modules');
|
|
6
6
|
if (!module.paths.includes(agentNodeModules)) {
|
|
7
7
|
module.paths.unshift(agentNodeModules);
|
|
8
8
|
}
|
package/examples/test-tg.js
CHANGED
|
@@ -5,7 +5,7 @@ async function main() {
|
|
|
5
5
|
const framework = new Framework({ debug: true });
|
|
6
6
|
|
|
7
7
|
await framework.bootstrap({
|
|
8
|
-
agentDir: process.cwd() + '/.
|
|
8
|
+
agentDir: process.cwd() + '/.foliko',
|
|
9
9
|
aiConfig: {
|
|
10
10
|
provider: process.env.FOLIKO_PROVIDER || 'minimax',
|
|
11
11
|
model: process.env.FOLIKO_MODEL || 'MiniMax-M2.7',
|
package/examples/test-think.js
CHANGED
|
@@ -5,7 +5,7 @@ async function main() {
|
|
|
5
5
|
const framework = new Framework({ debug: false });
|
|
6
6
|
|
|
7
7
|
await framework.bootstrap({
|
|
8
|
-
agentDir: process.cwd() + '/.
|
|
8
|
+
agentDir: process.cwd() + '/.foliko',
|
|
9
9
|
aiConfig: {
|
|
10
10
|
provider: process.env.FOLIKO_PROVIDER || 'minimax',
|
|
11
11
|
model: process.env.FOLIKO_MODEL || 'MiniMax-M2.7',
|
|
@@ -12,7 +12,7 @@ async function main() {
|
|
|
12
12
|
|
|
13
13
|
// Bootstrap 加载默认插件
|
|
14
14
|
await framework.bootstrap({
|
|
15
|
-
agentDir: './.
|
|
15
|
+
agentDir: './.foliko',
|
|
16
16
|
aiConfig: {
|
|
17
17
|
provider: 'deepseek',
|
|
18
18
|
model: 'deepseek-chat',
|
|
@@ -68,7 +68,7 @@ async function main() {
|
|
|
68
68
|
|
|
69
69
|
try {
|
|
70
70
|
// 使用 chat() 而非 chatStream()
|
|
71
|
-
const result = await sessionInfo.
|
|
71
|
+
const result = await sessionInfo.foliko.chat(mockText, {
|
|
72
72
|
sessionId: sessionInfo.sessionId,
|
|
73
73
|
});
|
|
74
74
|
|
|
@@ -83,7 +83,7 @@ async function main() {
|
|
|
83
83
|
console.log('\n--- Test chatStream() for comparison ---');
|
|
84
84
|
try {
|
|
85
85
|
let fullResponse = '';
|
|
86
|
-
for await (const chunk of sessionInfo.
|
|
86
|
+
for await (const chunk of sessionInfo.foliko.chatStream('今天天气怎么样?', {
|
|
87
87
|
sessionId: sessionInfo.sessionId,
|
|
88
88
|
})) {
|
|
89
89
|
if (chunk.type === 'text' && chunk.text) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "foliko",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.69",
|
|
4
4
|
"description": "简约的插件化 Agent 框架",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"type": "commonjs",
|
|
@@ -81,6 +81,8 @@
|
|
|
81
81
|
"node-schedule": "^2.1.1",
|
|
82
82
|
"node-telegram-bot-api": "^0.67.0",
|
|
83
83
|
"nodemailer": "^6.10.0",
|
|
84
|
+
"pino": "^10.3.1",
|
|
85
|
+
"pino-pretty": "^13.1.3",
|
|
84
86
|
"qrcode-terminal": "^0.12.0",
|
|
85
87
|
"remove-markdown": "^0.6.3",
|
|
86
88
|
"zod": "^3.25.76"
|
|
@@ -32,7 +32,7 @@ class AmbientAgentPlugin extends Plugin {
|
|
|
32
32
|
enabled: config.enabled !== false,
|
|
33
33
|
tickInterval: config.tickInterval || 5000, // tick间隔(毫秒)
|
|
34
34
|
cooldownPeriod: config.cooldownPeriod || 3000, // 冷却时间(毫秒)
|
|
35
|
-
persistencePath: config.persistencePath || '.
|
|
35
|
+
persistencePath: config.persistencePath || '.foliko/data/ambient',
|
|
36
36
|
defaultGoals: config.defaultGoals || []
|
|
37
37
|
}
|
|
38
38
|
|
package/plugins/audit-plugin.js
CHANGED
|
@@ -1,36 +1,72 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Audit 审计日志插件
|
|
3
3
|
* 记录所有操作日志,支持日志查询和统计
|
|
4
|
+
* 基于 Pino 日志系统
|
|
4
5
|
*/
|
|
5
6
|
|
|
6
7
|
const { Plugin } = require('../src/core/plugin-base')
|
|
8
|
+
const { logger } = require('../src/utils/logger')
|
|
7
9
|
const { z } = require('zod')
|
|
10
|
+
const path = require('path')
|
|
11
|
+
const fs = require('fs')
|
|
12
|
+
const os = require('os')
|
|
8
13
|
|
|
9
14
|
class AuditPlugin extends Plugin {
|
|
10
15
|
constructor(config = {}) {
|
|
11
16
|
super()
|
|
12
17
|
this.name = 'audit'
|
|
13
|
-
this.version = '
|
|
18
|
+
this.version = '2.0.0'
|
|
14
19
|
this.description = '审计日志插件,记录工具调用、技能执行、错误信息等操作历史'
|
|
15
20
|
this.priority = 20
|
|
16
|
-
this.system = true
|
|
21
|
+
this.system = true
|
|
17
22
|
|
|
18
23
|
this.config = {
|
|
19
24
|
maxLogs: config.maxLogs || 1000,
|
|
20
25
|
retentionDays: config.retentionDays || 30,
|
|
21
|
-
logDir: config.logDir || '.agent
|
|
26
|
+
logDir: config.logDir || path.join(os.homedir(), '.vb-agent', 'logs', 'audit')
|
|
22
27
|
}
|
|
23
28
|
|
|
24
29
|
this._framework = null
|
|
25
|
-
this._logs = []
|
|
26
30
|
this._logIdCounter = 0
|
|
31
|
+
this._auditLogger = null
|
|
32
|
+
this._auditLogFile = null
|
|
33
|
+
this._queryLogs = [] // 内存缓存用于查询
|
|
34
|
+
this._initAuditLogger()
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* 初始化审计日志器
|
|
39
|
+
*/
|
|
40
|
+
_initAuditLogger() {
|
|
41
|
+
const pino = require('pino')
|
|
42
|
+
|
|
43
|
+
// 确保目录存在
|
|
44
|
+
if (!fs.existsSync(this.config.logDir)) {
|
|
45
|
+
fs.mkdirSync(this.config.logDir, { recursive: true })
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// 审计日志文件名
|
|
49
|
+
const date = new Date().toISOString().split('T')[0]
|
|
50
|
+
this._auditLogFile = path.join(this.config.logDir, `audit-${date}.jsonl`)
|
|
51
|
+
|
|
52
|
+
// 创建文件写入流
|
|
53
|
+
const fileStream = fs.createWriteStream(this._auditLogFile, { flags: 'a' })
|
|
54
|
+
|
|
55
|
+
// 创建专门的审计 logger
|
|
56
|
+
this._auditLogger = pino({
|
|
57
|
+
name: 'audit',
|
|
58
|
+
level: 'info',
|
|
59
|
+
timestamp: pino.stdTimeFunctions.isoTime,
|
|
60
|
+
formatters: {
|
|
61
|
+
level: (label) => label.toUpperCase(),
|
|
62
|
+
},
|
|
63
|
+
}, fileStream)
|
|
27
64
|
}
|
|
28
65
|
|
|
29
66
|
install(framework) {
|
|
30
67
|
this._framework = framework
|
|
31
68
|
|
|
32
69
|
// 监听框架事件
|
|
33
|
-
// tool:call 和 tool-call 两种格式都监听(兼容不同来源)
|
|
34
70
|
framework.on('tool:call', (data) => this._log('tool_call', data))
|
|
35
71
|
framework.on('tool-call', (data) => this._log('tool_call', data))
|
|
36
72
|
framework.on('tool:result', (data) => this._log('tool_result', data))
|
|
@@ -51,10 +87,11 @@ class AuditPlugin extends Plugin {
|
|
|
51
87
|
offset: z.number().optional().describe('偏移量,默认 0')
|
|
52
88
|
}),
|
|
53
89
|
execute: async (args) => {
|
|
54
|
-
|
|
90
|
+
const logs = this._queryLogs
|
|
55
91
|
|
|
92
|
+
let filtered = logs
|
|
56
93
|
if (args.type) {
|
|
57
|
-
|
|
94
|
+
filtered = filtered.filter(log => log.type === args.type)
|
|
58
95
|
}
|
|
59
96
|
|
|
60
97
|
const offset = args.offset || 0
|
|
@@ -62,9 +99,9 @@ class AuditPlugin extends Plugin {
|
|
|
62
99
|
|
|
63
100
|
return {
|
|
64
101
|
success: true,
|
|
65
|
-
data:
|
|
102
|
+
data: filtered.slice(-limit - offset, filtered.length - offset),
|
|
66
103
|
metadata: {
|
|
67
|
-
total:
|
|
104
|
+
total: filtered.length,
|
|
68
105
|
offset,
|
|
69
106
|
limit
|
|
70
107
|
}
|
|
@@ -111,21 +148,30 @@ class AuditPlugin extends Plugin {
|
|
|
111
148
|
|
|
112
149
|
/**
|
|
113
150
|
* 记录日志
|
|
114
|
-
* @private
|
|
115
151
|
*/
|
|
116
152
|
_log(type, data) {
|
|
117
153
|
const log = {
|
|
118
154
|
id: ++this._logIdCounter,
|
|
119
155
|
type,
|
|
120
156
|
data,
|
|
121
|
-
timestamp: new Date()
|
|
157
|
+
timestamp: new Date().toISOString()
|
|
122
158
|
}
|
|
123
159
|
|
|
124
|
-
|
|
160
|
+
// 写入审计日志文件
|
|
161
|
+
if (this._auditLogger) {
|
|
162
|
+
this._auditLogger.info({
|
|
163
|
+
auditId: log.id,
|
|
164
|
+
auditType: log.type,
|
|
165
|
+
auditData: log.data
|
|
166
|
+
}, 'audit event')
|
|
167
|
+
}
|
|
125
168
|
|
|
126
|
-
//
|
|
127
|
-
|
|
128
|
-
|
|
169
|
+
// 缓存到内存用于查询
|
|
170
|
+
this._queryLogs.push(log)
|
|
171
|
+
|
|
172
|
+
// 限制缓存数量
|
|
173
|
+
if (this._queryLogs.length > this.config.maxLogs) {
|
|
174
|
+
this._queryLogs = this._queryLogs.slice(-this.config.maxLogs)
|
|
129
175
|
}
|
|
130
176
|
|
|
131
177
|
return log
|
|
@@ -136,16 +182,17 @@ class AuditPlugin extends Plugin {
|
|
|
136
182
|
*/
|
|
137
183
|
getStats() {
|
|
138
184
|
const counts = {}
|
|
139
|
-
for (const log of this.
|
|
185
|
+
for (const log of this._queryLogs) {
|
|
140
186
|
counts[log.type] = (counts[log.type] || 0) + 1
|
|
141
187
|
}
|
|
142
188
|
|
|
143
189
|
return {
|
|
144
|
-
total: this.
|
|
190
|
+
total: this._queryLogs.length,
|
|
145
191
|
maxLogs: this.config.maxLogs,
|
|
146
192
|
counts,
|
|
147
|
-
oldest: this.
|
|
148
|
-
newest: this.
|
|
193
|
+
oldest: this._queryLogs.length > 0 ? this._queryLogs[0].timestamp : null,
|
|
194
|
+
newest: this._queryLogs.length > 0 ? this._queryLogs[this._queryLogs.length - 1].timestamp : null,
|
|
195
|
+
logFile: this._auditLogFile
|
|
149
196
|
}
|
|
150
197
|
}
|
|
151
198
|
|
|
@@ -153,7 +200,7 @@ class AuditPlugin extends Plugin {
|
|
|
153
200
|
* 导出日志
|
|
154
201
|
*/
|
|
155
202
|
exportLogs(options = {}) {
|
|
156
|
-
let logs = [...this.
|
|
203
|
+
let logs = [...this._queryLogs]
|
|
157
204
|
|
|
158
205
|
// 按类型过滤
|
|
159
206
|
if (options.type) {
|
|
@@ -163,12 +210,12 @@ class AuditPlugin extends Plugin {
|
|
|
163
210
|
// 按日期过滤
|
|
164
211
|
if (options.startDate) {
|
|
165
212
|
const start = new Date(options.startDate)
|
|
166
|
-
logs = logs.filter(log => log.timestamp >= start)
|
|
213
|
+
logs = logs.filter(log => new Date(log.timestamp) >= start)
|
|
167
214
|
}
|
|
168
215
|
|
|
169
216
|
if (options.endDate) {
|
|
170
217
|
const end = new Date(options.endDate)
|
|
171
|
-
logs = logs.filter(log => log.timestamp <= end)
|
|
218
|
+
logs = logs.filter(log => new Date(log.timestamp) <= end)
|
|
172
219
|
}
|
|
173
220
|
|
|
174
221
|
return { logs }
|
|
@@ -186,9 +233,13 @@ class AuditPlugin extends Plugin {
|
|
|
186
233
|
}
|
|
187
234
|
|
|
188
235
|
uninstall(framework) {
|
|
189
|
-
this.
|
|
236
|
+
this._queryLogs = []
|
|
237
|
+
if (this._auditLogger) {
|
|
238
|
+
// 关闭文件流
|
|
239
|
+
}
|
|
240
|
+
this._auditLogger = null
|
|
190
241
|
this._framework = null
|
|
191
242
|
}
|
|
192
243
|
}
|
|
193
244
|
|
|
194
|
-
module.exports = AuditPlugin
|
|
245
|
+
module.exports = AuditPlugin
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* 默认插件配置加载器
|
|
3
|
-
* 检测 .
|
|
3
|
+
* 检测 .foliko/ 目录下的配置,提供给 bootstrap() 使用
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
const fs = require('fs')
|
|
@@ -11,10 +11,10 @@ const log = logger.child('AgentConfig')
|
|
|
11
11
|
const bootstrapLog = logger.child('Bootstrap')
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
|
-
* 加载 .
|
|
14
|
+
* 加载 .foliko 目录下的配置
|
|
15
15
|
* 返回配置对象,可用于手动加载插件
|
|
16
16
|
*/
|
|
17
|
-
function loadAgentConfig(agentDir = '.
|
|
17
|
+
function loadAgentConfig(agentDir = '.foliko') {
|
|
18
18
|
const config = {
|
|
19
19
|
agentDir,
|
|
20
20
|
ai: {},
|
|
@@ -28,7 +28,7 @@ function loadAgentConfig(agentDir = '.agent') {
|
|
|
28
28
|
const resolvedDir = path.resolve(process.cwd(), agentDir)
|
|
29
29
|
|
|
30
30
|
if (!fs.existsSync(resolvedDir)) {
|
|
31
|
-
log.info(` .
|
|
31
|
+
log.info(` .foliko directory not found: ${resolvedDir}`)
|
|
32
32
|
return config
|
|
33
33
|
}
|
|
34
34
|
|
|
@@ -136,7 +136,7 @@ function loadAgentConfig(agentDir = '.agent') {
|
|
|
136
136
|
}
|
|
137
137
|
}
|
|
138
138
|
|
|
139
|
-
// 添加 .
|
|
139
|
+
// 添加 .foliko/skills 目录(不存在则创建)
|
|
140
140
|
const skillsDir = path.join(resolvedDir, 'skills')
|
|
141
141
|
if (fs.existsSync(resolvedDir) && !fs.existsSync(skillsDir)) {
|
|
142
142
|
fs.mkdirSync(skillsDir, { recursive: true })
|
|
@@ -146,7 +146,7 @@ function loadAgentConfig(agentDir = '.agent') {
|
|
|
146
146
|
config.skillsDirs.push(skillsDir)
|
|
147
147
|
}
|
|
148
148
|
|
|
149
|
-
// 添加 .
|
|
149
|
+
// 添加 .foliko/skills 目录(不存在则创建)
|
|
150
150
|
const cmdskillsDir = path.join(cmdDir, 'skills')
|
|
151
151
|
if (fs.existsSync(resolvedDir) && !fs.existsSync(cmdskillsDir)) {
|
|
152
152
|
fs.mkdirSync(cmdskillsDir, { recursive: true })
|
|
@@ -184,7 +184,7 @@ class DefaultPlugins extends Plugin {
|
|
|
184
184
|
this.system = true
|
|
185
185
|
|
|
186
186
|
this._framework = null
|
|
187
|
-
this._agentDir = config.agentDir || '.
|
|
187
|
+
this._agentDir = config.agentDir || '.foliko'
|
|
188
188
|
this._config = null
|
|
189
189
|
}
|
|
190
190
|
|
|
@@ -364,6 +364,16 @@ async function bootstrapDefaults(framework, config = {}) {
|
|
|
364
364
|
}
|
|
365
365
|
}
|
|
366
366
|
|
|
367
|
+
// 6. 大数据分拆插件
|
|
368
|
+
if (shouldLoad('data-splitter')) {
|
|
369
|
+
const { DataSplitterPlugin } = require('./data-splitter-plugin')
|
|
370
|
+
await framework.loadPlugin(new DataSplitterPlugin({
|
|
371
|
+
autoSplitThreshold: agentConfig.dataSplitter?.autoSplitThreshold || 50000,
|
|
372
|
+
chunkSize: agentConfig.dataSplitter?.chunkSize || 60000,
|
|
373
|
+
maxConcurrent: agentConfig.dataSplitter?.maxConcurrent || 3,
|
|
374
|
+
}))
|
|
375
|
+
framework._debug&&bootstrapLog.debug(' DataSplitter Plugin loaded')
|
|
376
|
+
}
|
|
367
377
|
|
|
368
378
|
// 7. 自动加载 plugins/ 目录下的所有插件
|
|
369
379
|
await loadCustomPlugins(framework, agentConfig)
|
|
@@ -383,8 +393,8 @@ async function bootstrapDefaults(framework, config = {}) {
|
|
|
383
393
|
/**
|
|
384
394
|
* 解析插件路径
|
|
385
395
|
* 支持两种结构:
|
|
386
|
-
* 1. 文件夹结构: .
|
|
387
|
-
* 2. 单文件结构: .
|
|
396
|
+
* 1. 文件夹结构: .foliko/plugins/my-plugin/index.js
|
|
397
|
+
* 2. 单文件结构: .foliko/plugins/my-plugin.js
|
|
388
398
|
* 支持符号链接
|
|
389
399
|
* @param {string} pluginsDir - 插件目录
|
|
390
400
|
* @param {string} name - 插件名称
|
|
@@ -502,10 +512,10 @@ async function loadCustomPlugins(framework, agentConfig) {
|
|
|
502
512
|
const { Plugin } = require('../src/core/plugin-base')
|
|
503
513
|
|
|
504
514
|
// 加载两个目录下的自定义插件:
|
|
505
|
-
// 1. .
|
|
515
|
+
// 1. .foliko/plugins/ - 用户自定义插件(强制启用)
|
|
506
516
|
// 2. plugins/ - 项目内置插件(自动加载)
|
|
507
517
|
const dirs = [
|
|
508
|
-
{ dir: path.resolve(process.cwd(), '.
|
|
518
|
+
{ dir: path.resolve(process.cwd(), '.foliko', 'plugins'), forceEnabled: true },
|
|
509
519
|
{ dir: path.resolve(process.cwd(), 'plugins'), forceEnabled: false },
|
|
510
520
|
{ dir: path.resolve(__dirname, '..', 'plugins'), forceEnabled: false }
|
|
511
521
|
// 项目下的 plugins 目录(兼容旧版本)
|
package/plugins/email/index.js
CHANGED
|
@@ -94,7 +94,7 @@ class EmailPlugin extends Plugin {
|
|
|
94
94
|
|
|
95
95
|
// 持久化存储
|
|
96
96
|
this._emailStore = null
|
|
97
|
-
this._persistencePath = config.persistencePath || '.
|
|
97
|
+
this._persistencePath = config.persistencePath || '.foliko/data/email/processed-emails.json'
|
|
98
98
|
|
|
99
99
|
// 防抖相关
|
|
100
100
|
this._emailDebounceMap = new Map() // msgId -> timer
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
const { Plugin } = require('../src/core/plugin-base');
|
|
7
7
|
const { logger } = require('../src/utils/logger');
|
|
8
8
|
const { z } = require('zod');
|
|
9
|
+
const { PROMPT_PRIORITY } = require('../src/core/constants');
|
|
9
10
|
const { zodSchemaToMarkdown,zodSchemaToTable } = require('@chnak/zod-to-markdown');
|
|
10
11
|
|
|
11
12
|
const log = logger.child('ExtensionExecutor');
|
|
@@ -268,7 +269,7 @@ class ExtensionExecutorPlugin extends Plugin {
|
|
|
268
269
|
}
|
|
269
270
|
|
|
270
271
|
// 注册扩展插件的提示词部分(使用基类方法)
|
|
271
|
-
this.registerPromptPart('extension-tools',
|
|
272
|
+
this.registerPromptPart('extension-tools', PROMPT_PRIORITY.EXTENSION_TOOLS, () => this._buildExtensionsDescription());
|
|
272
273
|
|
|
273
274
|
return this;
|
|
274
275
|
}
|
|
@@ -391,14 +392,41 @@ class ExtensionExecutorPlugin extends Plugin {
|
|
|
391
392
|
if (this._extensions.size > 0 || (this._mcpExecutor && Object.keys(this._mcpExecutor.tools || {}).length > 0)) {
|
|
392
393
|
desc += '## 【Extensions】扩展插件\n\n';
|
|
393
394
|
desc += '**使用流程(必须按顺序执行):**\n';
|
|
394
|
-
desc += '1. 调用 `ext_skill({
|
|
395
|
-
desc += '2. 根据返回的参数定义,使用 `ext_call({ plugin, tool, args })`
|
|
395
|
+
desc += '1. 调用 `ext_skill({ plugin: "skill" })` 获取技能命令的详细参数\n';
|
|
396
|
+
desc += '2. 根据返回的参数定义,使用 `ext_call({ plugin: "skill", tool: "技能名:命令名", args: {...} })` 调用\n\n';
|
|
396
397
|
desc += '> **警告**:禁止在未执行第1步获取参数的情况下直接调用 `ext_call`!\n\n';
|
|
397
398
|
|
|
398
399
|
for (const [name, ext] of this._extensions) {
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
400
|
+
if (name === 'skill') {
|
|
401
|
+
// Skill 命令按技能名分组显示
|
|
402
|
+
const toolsBySkill = {};
|
|
403
|
+
for (const tool of ext.tools) {
|
|
404
|
+
// 命令格式: skillname:cmdname
|
|
405
|
+
const parts = tool.name.split(':');
|
|
406
|
+
const skillName = parts[0];
|
|
407
|
+
const cmdName = parts.slice(1).join(':');
|
|
408
|
+
if (!toolsBySkill[skillName]) {
|
|
409
|
+
toolsBySkill[skillName] = [];
|
|
410
|
+
}
|
|
411
|
+
toolsBySkill[skillName].push({ name: cmdName, description: tool.description });
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
for (const [skillName, cmds] of Object.entries(toolsBySkill)) {
|
|
415
|
+
desc += `### skill.${skillName}\n`;
|
|
416
|
+
desc += `**调用方式:**\n`;
|
|
417
|
+
desc += `1. 先调用 \`loadSkill({ skill: "${skillName}" })\` 获取 \`${skillName}\` 技能的详细参数\n`;
|
|
418
|
+
desc += `2. 调用 \`ext_call({ plugin: "skill", tool: "${skillName}:<命令名>", args: { args: "参数" } })\`\n\n`;
|
|
419
|
+
desc += `**可用命令:**\n`;
|
|
420
|
+
for (const c of cmds) {
|
|
421
|
+
desc += `- \`${c.name}\`: ${c.description}\n`;
|
|
422
|
+
}
|
|
423
|
+
desc += '\n';
|
|
424
|
+
}
|
|
425
|
+
} else {
|
|
426
|
+
desc += `### ${ext.name || name}\n`;
|
|
427
|
+
desc += `${ext.description || '无描述'}\n`;
|
|
428
|
+
desc += `**工具:** ${ext.tools.map(t => `\`${t.name}\``).join(', ')}\n\n`;
|
|
429
|
+
}
|
|
402
430
|
}
|
|
403
431
|
|
|
404
432
|
// MCP 服务器工具
|
|
@@ -534,6 +562,56 @@ class ExtensionExecutorPlugin extends Plugin {
|
|
|
534
562
|
}));
|
|
535
563
|
}
|
|
536
564
|
|
|
565
|
+
/**
|
|
566
|
+
* 获取 skill 命令列表(用于 autocomplete)
|
|
567
|
+
*/
|
|
568
|
+
getSkillCommands() {
|
|
569
|
+
const ext = this._extensions.get('skill');
|
|
570
|
+
if (!ext) return [];
|
|
571
|
+
return ext.tools.map(t => ({
|
|
572
|
+
name: t.name.replace(/^([^:]+):(.*)$/, '$1:$2'),
|
|
573
|
+
description: t.description || t.name,
|
|
574
|
+
}));
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
/**
|
|
578
|
+
* 获取 skill 命令帮助文本
|
|
579
|
+
*/
|
|
580
|
+
getSkillCommandsHelp() {
|
|
581
|
+
const ext = this._extensions.get('skill');
|
|
582
|
+
if (!ext || ext.tools.length === 0) return '';
|
|
583
|
+
|
|
584
|
+
const lines = [];
|
|
585
|
+
for (const tool of ext.tools) {
|
|
586
|
+
const parts = tool.name.split(':');
|
|
587
|
+
if (parts.length === 2) {
|
|
588
|
+
lines.push(`/${parts[0]}:${parts[1]} - ${tool.description || '无描述'}`);
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
return lines.join('\n');
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
/**
|
|
595
|
+
* 检查并执行 skill 命令
|
|
596
|
+
* @param {string} command - 命令名(如 "skillname:cmdname")
|
|
597
|
+
* @param {string} args - 命令参数
|
|
598
|
+
* @returns {Promise<object|null>} 执行结果,失败返回 null
|
|
599
|
+
*/
|
|
600
|
+
async executeSkillCommand(command, args) {
|
|
601
|
+
if (!command.includes(':')) return null;
|
|
602
|
+
try {
|
|
603
|
+
const result = await this._framework.executeTool('ext_call', {
|
|
604
|
+
plugin: 'skill',
|
|
605
|
+
tool: command,
|
|
606
|
+
args: { args }
|
|
607
|
+
});
|
|
608
|
+
return result.success !== false ? result : null;
|
|
609
|
+
} catch (err) {
|
|
610
|
+
log.warn('Skill command failed:', err.message);
|
|
611
|
+
return null;
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
|
|
537
615
|
async reload(framework) {
|
|
538
616
|
this._framework = framework;
|
|
539
617
|
// 重新扫描所有已加载插件的 tools
|
|
@@ -559,7 +637,7 @@ class ExtensionExecutorPlugin extends Plugin {
|
|
|
559
637
|
if (!mcpExecutor) return;
|
|
560
638
|
|
|
561
639
|
try {
|
|
562
|
-
const configPath = path.resolve('.
|
|
640
|
+
const configPath = path.resolve('.foliko/mcp_config.json');
|
|
563
641
|
if (fs.existsSync(configPath)) {
|
|
564
642
|
const configContent = fs.readFileSync(configPath, 'utf8');
|
|
565
643
|
const config = JSON.parse(configContent);
|