foliko 1.0.25 → 1.0.26
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 +7 -1
- package/README.md +114 -29
- package/cli/src/commands/chat.js +0 -12
- package/package.json +1 -1
- package/plugins/ai-plugin.js +0 -7
- package/plugins/default-plugins.js +9 -22
- package/plugins/subagent-plugin.js +65 -48
- package/plugins/telegram-plugin.js +2 -0
- package/plugins/tools-plugin.js +6 -4
- package/plugins/weixin-plugin.js +5 -3
- package/src/core/plugin-base.js +7 -0
- package/src/core/plugin-manager.js +70 -4
|
@@ -57,7 +57,13 @@
|
|
|
57
57
|
"Bash(node -e \"const { WeixinBot } = require\\('@chnak/weixin-bot'\\); console.log\\(typeof WeixinBot\\)\" 2>&1)",
|
|
58
58
|
"Bash(node -e \"import\\('@chnak/weixin-bot'\\).then\\(m => console.log\\('OK:', typeof m.WeixinBot\\)\\).catch\\(e => console.error\\('Error:', e.message\\)\\)\" 2>&1)",
|
|
59
59
|
"Bash(ls -la D:/code/vb-agent/cli/bin/ && cat D:/code/vb-agent/cli/bin/*.js 2>/dev/null | head -50)",
|
|
60
|
-
"Bash(npm config:*)"
|
|
60
|
+
"Bash(npm config:*)",
|
|
61
|
+
"Bash(node -c plugins/subagent-plugin.js 2>&1)",
|
|
62
|
+
"Bash(node -c plugins/default-plugins.js 2>&1)",
|
|
63
|
+
"Bash(node -c plugins/default-plugins.js && node -c src/core/plugin-manager.js && node -c plugins/tools-plugin.js 2>&1)",
|
|
64
|
+
"Bash(node -c src/core/plugin-base.js && node -c src/core/plugin-manager.js 2>&1)",
|
|
65
|
+
"Bash(node -c plugins/telegram-plugin.js && node -c plugins/weixin-plugin.js 2>&1)",
|
|
66
|
+
"Bash(node -c src/core/plugin-manager.js 2>&1)"
|
|
61
67
|
]
|
|
62
68
|
}
|
|
63
69
|
}
|
package/README.md
CHANGED
|
@@ -11,15 +11,42 @@
|
|
|
11
11
|
- **技能系统** - 可扩展的 Skill 管理
|
|
12
12
|
- **会话管理** - 支持多会话切换
|
|
13
13
|
- **规则引擎** - 可配置的行为规则
|
|
14
|
+
- **子 Agent** - 支持多子 Agent 分工协作
|
|
15
|
+
|
|
16
|
+
## 安装
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
# 方式一:npm 安装
|
|
20
|
+
npm install -g foliko
|
|
21
|
+
|
|
22
|
+
# 方式二:Windows 一键脚本安装
|
|
23
|
+
irm https://folikoai.com/install.ps1 | iex
|
|
24
|
+
|
|
25
|
+
# 方式三:Mac/Linux 一键脚本安装
|
|
26
|
+
curl -fsSL https://folikoai.com/install.sh | bash
|
|
27
|
+
```
|
|
14
28
|
|
|
15
29
|
## 快速开始
|
|
16
30
|
|
|
17
31
|
```bash
|
|
18
|
-
#
|
|
19
|
-
|
|
32
|
+
# 全局安装后
|
|
33
|
+
foliko chat
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## CLI 命令
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
# 聊天模式(使用 .env 中的配置)
|
|
40
|
+
foliko chat
|
|
41
|
+
|
|
42
|
+
# 指定完整配置
|
|
43
|
+
foliko chat --provider deepseek --model deepseek-chat --base-url https://api.deepseek.com/v1 --api-key sk-xxx
|
|
44
|
+
|
|
45
|
+
# 只指定 provider(使用 provider 默认 model 和 baseURL)
|
|
46
|
+
foliko chat --provider deepseek --api-key sk-xxx
|
|
20
47
|
|
|
21
|
-
#
|
|
22
|
-
|
|
48
|
+
# 列出所有子Agent配置
|
|
49
|
+
foliko list
|
|
23
50
|
```
|
|
24
51
|
|
|
25
52
|
## 项目结构
|
|
@@ -30,7 +57,7 @@ foliko/
|
|
|
30
57
|
│ └── bin/foliko.js # CLI 入口
|
|
31
58
|
├── src/ # 核心框架
|
|
32
59
|
│ ├── core/ # 核心组件
|
|
33
|
-
│ ├── capabilities/
|
|
60
|
+
│ ├── capabilities/ # 能力插件
|
|
34
61
|
│ └── executors/ # 执行器
|
|
35
62
|
├── plugins/ # 内置插件
|
|
36
63
|
├── skills/ # 技能目录
|
|
@@ -50,19 +77,19 @@ FOLIKO_PROVIDER=minimax
|
|
|
50
77
|
# AI Model(可选,不填则使用 provider 默认值)
|
|
51
78
|
# MiniMax: MiniMax-M2.7
|
|
52
79
|
# DeepSeek: deepseek-chat, deepseek-coder 等
|
|
53
|
-
FOLIKO_MODEL=
|
|
80
|
+
FOLIKO_MODEL=MiniMax-M2.7
|
|
54
81
|
|
|
55
82
|
# API Base URL(可选,不填则使用 provider 默认值)
|
|
56
83
|
# MiniMax: https://api.minimaxi.com/v1
|
|
57
84
|
# DeepSeek: https://api.deepseek.com/v1
|
|
58
|
-
FOLIKO_BASE_URL=
|
|
85
|
+
FOLIKO_BASE_URL=https://api.minimaxi.com/v1
|
|
59
86
|
|
|
60
87
|
# API Key(通用,不填则使用 provider 专用 key)
|
|
61
|
-
FOLIKO_API_KEY=
|
|
88
|
+
FOLIKO_API_KEY=sk-your-minimax-api-key
|
|
62
89
|
|
|
63
90
|
# Provider 专用 API Key(可选)
|
|
64
|
-
DEEPSEEK_API_KEY=sk-your-deepseek-api-key
|
|
65
|
-
MINIMAX_API_KEY=sk-your-minimax-api-key
|
|
91
|
+
# DEEPSEEK_API_KEY=sk-your-deepseek-api-key
|
|
92
|
+
# MINIMAX_API_KEY=sk-your-minimax-api-key
|
|
66
93
|
```
|
|
67
94
|
|
|
68
95
|
**配置优先级**:命令行参数 > .env配置 > provider默认值
|
|
@@ -74,11 +101,49 @@ MINIMAX_API_KEY=sk-your-minimax-api-key
|
|
|
74
101
|
├── config # 配置文件
|
|
75
102
|
├── ai.json # AI 配置
|
|
76
103
|
├── mcp_config.json # MCP 服务器配置
|
|
104
|
+
├── agents/ # 子Agent配置目录
|
|
77
105
|
├── plugins/ # 用户插件目录
|
|
78
106
|
├── skills/ # 用户技能目录
|
|
79
107
|
└── data/ # 数据目录
|
|
80
108
|
```
|
|
81
109
|
|
|
110
|
+
### 子 Agent 配置 (.agent/agents/)
|
|
111
|
+
|
|
112
|
+
在 `.agent/agents/` 目录下放置子 Agent 配置文件,支持 `.js`、`.json`、`.md` 格式:
|
|
113
|
+
|
|
114
|
+
```
|
|
115
|
+
.agent/agents/
|
|
116
|
+
├── backend-specialist.md
|
|
117
|
+
├── frontend-specialist.md
|
|
118
|
+
└── database-architect.md
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
#### JSON 格式
|
|
122
|
+
|
|
123
|
+
```json
|
|
124
|
+
{
|
|
125
|
+
"name": "backend-specialist",
|
|
126
|
+
"role": "Backend Specialist",
|
|
127
|
+
"description": "Expert in backend development",
|
|
128
|
+
"parentTools": ["read_file", "run_command"]
|
|
129
|
+
}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
#### Markdown 格式
|
|
133
|
+
|
|
134
|
+
```markdown
|
|
135
|
+
# Backend Specialist
|
|
136
|
+
|
|
137
|
+
```json
|
|
138
|
+
{
|
|
139
|
+
"name": "backend-specialist",
|
|
140
|
+
"role": "Backend Specialist",
|
|
141
|
+
"description": "Expert in backend development",
|
|
142
|
+
"parentTools": ["read_file", "run_command"]
|
|
143
|
+
}
|
|
144
|
+
```
|
|
145
|
+
```
|
|
146
|
+
|
|
82
147
|
### ai.json 格式
|
|
83
148
|
|
|
84
149
|
```json
|
|
@@ -191,37 +256,57 @@ allowed-tools: tool1,tool2
|
|
|
191
256
|
技能内容...
|
|
192
257
|
```
|
|
193
258
|
|
|
194
|
-
## CLI 命令
|
|
195
|
-
|
|
196
|
-
```bash
|
|
197
|
-
# 聊天模式(使用 .env 中的配置)
|
|
198
|
-
npm run chat
|
|
199
|
-
|
|
200
|
-
# 指定完整配置
|
|
201
|
-
foliko chat --provider deepseek --model deepseek-chat --base-url https://api.deepseek.com/v1 --api-key sk-xxx
|
|
202
|
-
|
|
203
|
-
# 只指定 provider(使用 provider 默认 model 和 baseURL)
|
|
204
|
-
foliko chat --provider deepseek --api-key sk-xxx
|
|
205
|
-
|
|
206
|
-
# 只指定 api-key(使用 FOLIKO_PROVIDER 设定的 provider)
|
|
207
|
-
foliko chat --api-key sk-xxx
|
|
208
|
-
```
|
|
209
|
-
|
|
210
259
|
## 内置工具
|
|
211
260
|
|
|
261
|
+
### 通用工具
|
|
262
|
+
|
|
212
263
|
| 工具 | 说明 |
|
|
213
264
|
|------|------|
|
|
214
265
|
| `loadSkill` | 加载技能 |
|
|
215
|
-
| `
|
|
216
|
-
| `
|
|
217
|
-
| `
|
|
266
|
+
| `list_plugins` | 列出所有插件 |
|
|
267
|
+
| `list_tools` | 列出所有工具 |
|
|
268
|
+
| `reload_plugins` | 重载插件 |
|
|
269
|
+
| `shell` | 执行 Shell 命令 |
|
|
270
|
+
| `powershell` | 执行 PowerShell 命令 |
|
|
271
|
+
| `python-execute` | 执行 Python 代码 |
|
|
272
|
+
| `python_script` | 执行 Python 脚本 |
|
|
273
|
+
| `pip_install` | 安装 Python 包 |
|
|
218
274
|
| `install` | 安装 npm 包 |
|
|
219
275
|
| `mcp_reload` | 重载 MCP 服务器 |
|
|
220
276
|
| `audit_query` | 查询审计日志 |
|
|
221
277
|
|
|
278
|
+
### 子 Agent 管理工具
|
|
279
|
+
|
|
280
|
+
| 工具 | 说明 |
|
|
281
|
+
|------|------|
|
|
282
|
+
| `subagent_list` | 列出所有子Agent |
|
|
283
|
+
| `subagent_call` | 调用指定的子Agent处理任务 |
|
|
284
|
+
| `subagent_reload` | 重新加载子Agent配置 |
|
|
285
|
+
|
|
286
|
+
### 会话工具
|
|
287
|
+
|
|
288
|
+
| 工具 | 说明 |
|
|
289
|
+
|------|------|
|
|
290
|
+
| `session_create` | 创建会话 |
|
|
291
|
+
| `session_get` | 获取会话 |
|
|
292
|
+
| `session_list` | 列出所有会话 |
|
|
293
|
+
| `session_delete` | 删除会话 |
|
|
294
|
+
| `session_history` | 获取会话历史 |
|
|
295
|
+
|
|
296
|
+
### 定时任务工具
|
|
297
|
+
|
|
298
|
+
| 工具 | 说明 |
|
|
299
|
+
|------|------|
|
|
300
|
+
| `schedule_task` | 创建定时任务 |
|
|
301
|
+
| `schedule_list` | 列出定时任务 |
|
|
302
|
+
| `schedule_cancel` | 取消定时任务 |
|
|
303
|
+
| `cron_examples` | 显示 cron 示例 |
|
|
304
|
+
|
|
222
305
|
## 工作目录
|
|
223
306
|
|
|
224
307
|
CLI 工作目录默认使用执行命令的目录。
|
|
308
|
+
- `.agent/` 目录会创建在工作目录下
|
|
309
|
+
- 配置文件、插件、技能等都放在工作目录的 `.agent/` 下
|
|
225
310
|
|
|
226
311
|
## 许可证
|
|
227
312
|
|
package/cli/src/commands/chat.js
CHANGED
|
@@ -81,20 +81,8 @@ function parseArgs(args) {
|
|
|
81
81
|
* Chat 命令入口
|
|
82
82
|
*/
|
|
83
83
|
async function chatCommand(args) {
|
|
84
|
-
console.log('[chatCommand] Starting with env:', {
|
|
85
|
-
FOLIKO_PROVIDER: process.env.FOLIKO_PROVIDER,
|
|
86
|
-
FOLIKO_MODEL: process.env.FOLIKO_MODEL,
|
|
87
|
-
MINIMAX_API_KEY: process.env.MINIMAX_API_KEY ? '***' + process.env.MINIMAX_API_KEY.slice(-4) : 'null'
|
|
88
|
-
})
|
|
89
84
|
|
|
90
85
|
const options = parseArgs(args)
|
|
91
|
-
console.log('[chatCommand] Parsed options:', {
|
|
92
|
-
provider: options.provider,
|
|
93
|
-
model: options.model,
|
|
94
|
-
baseURL: options.baseURL,
|
|
95
|
-
apiKey: options.apiKey ? '***' + options.apiKey.slice(-4) : 'null'
|
|
96
|
-
})
|
|
97
|
-
|
|
98
86
|
console.log('=== Foliko 持续对话 ===\n')
|
|
99
87
|
console.log('输入 exit 或 quit 退出\n')
|
|
100
88
|
|
package/package.json
CHANGED
package/plugins/ai-plugin.js
CHANGED
|
@@ -38,13 +38,6 @@ class AIPlugin extends Plugin {
|
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
_initAIClient() {
|
|
41
|
-
console.log('[AIPlugin] initAIClient called')
|
|
42
|
-
console.log('[AIPlugin] config:', JSON.stringify({
|
|
43
|
-
provider: this.config.provider,
|
|
44
|
-
model: this.config.model,
|
|
45
|
-
apiKey: this.config.apiKey ? '***' + this.config.apiKey.slice(-4) : 'null',
|
|
46
|
-
baseURL: this.config.baseURL
|
|
47
|
-
}))
|
|
48
41
|
|
|
49
42
|
if (!this.config.apiKey) {
|
|
50
43
|
console.warn('[AIPlugin] No API key provided, AI features disabled')
|
|
@@ -214,7 +214,7 @@ async function bootstrapDefaults(framework, config = {}) {
|
|
|
214
214
|
'install', 'ai', 'storage', 'tools', 'workflow', 'skill-manager',
|
|
215
215
|
'mcp-executor', 'shell-executor', 'python-executor', 'session',
|
|
216
216
|
'audit', 'rules', 'scheduler', 'file-system', 'think',
|
|
217
|
-
'python-plugin-loader', 'telegram', 'weixin'
|
|
217
|
+
'python-plugin-loader', 'telegram', 'weixin'
|
|
218
218
|
])
|
|
219
219
|
|
|
220
220
|
// 辅助函数:检查插件是否应该加载(核心插件不能禁用)
|
|
@@ -393,19 +393,18 @@ async function bootstrapDefaults(framework, config = {}) {
|
|
|
393
393
|
console.log('[Bootstrap] Python Plugin Loader loaded')
|
|
394
394
|
}
|
|
395
395
|
|
|
396
|
-
// 12.6 Telegram
|
|
396
|
+
// 12.6 Telegram 插件(默认禁用,需要在 plugins.json 中设置 enabled: true)
|
|
397
397
|
if (shouldLoad('telegram')) {
|
|
398
|
-
const
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
await framework.loadPlugin(new TelegramPlugin({ botToken: telegramToken }))
|
|
404
|
-
console.log('[Bootstrap] Telegram Plugin loaded')
|
|
398
|
+
const { Plugin } = require('../src/core/plugin-base')
|
|
399
|
+
const createTelegramPlugin = require('./telegram-plugin')
|
|
400
|
+
const TelegramPlugin = createTelegramPlugin(Plugin)
|
|
401
|
+
const telegramConfig = {
|
|
402
|
+
botToken: process.env.TELEGRAM_BOT_TOKEN || agentConfig.telegram?.botToken
|
|
405
403
|
}
|
|
404
|
+
await framework.loadPlugin(new TelegramPlugin(telegramConfig))
|
|
406
405
|
}
|
|
407
406
|
|
|
408
|
-
// 12.7 WeChat
|
|
407
|
+
// 12.7 WeChat 插件(默认禁用,需要在 plugins.json 中设置 enabled: true)
|
|
409
408
|
if (shouldLoad('weixin')) {
|
|
410
409
|
try {
|
|
411
410
|
const { Plugin } = require('../src/core/plugin-base')
|
|
@@ -417,23 +416,11 @@ async function bootstrapDefaults(framework, config = {}) {
|
|
|
417
416
|
allowedUsers: agentConfig.weixin?.allowedUsers || []
|
|
418
417
|
}
|
|
419
418
|
await framework.loadPlugin(new WeixinPlugin(weixinConfig))
|
|
420
|
-
console.log('[Bootstrap] WeChat Plugin loaded')
|
|
421
419
|
} catch (err) {
|
|
422
420
|
console.warn('[Bootstrap] WeChat Plugin not available:', err.message)
|
|
423
421
|
}
|
|
424
422
|
}
|
|
425
423
|
|
|
426
|
-
// 12.8 SubAgent 管理器(加载 .agent/agents 目录下的子Agent)
|
|
427
|
-
if (shouldLoad('subagent-manager')) {
|
|
428
|
-
try {
|
|
429
|
-
const { SubAgentManagerPlugin } = require('./subagent-plugin')
|
|
430
|
-
await framework.loadPlugin(new SubAgentManagerPlugin({ agentsDir: agentConfig.agentsDir }))
|
|
431
|
-
console.log('[Bootstrap] SubAgent Manager loaded')
|
|
432
|
-
} catch (err) {
|
|
433
|
-
console.warn('[Bootstrap] SubAgent Manager failed:', err.message)
|
|
434
|
-
}
|
|
435
|
-
}
|
|
436
|
-
|
|
437
424
|
// 13. 加载自定义插件
|
|
438
425
|
await loadCustomPlugins(framework, agentConfig)
|
|
439
426
|
|
|
@@ -40,7 +40,7 @@ class SubAgentPlugin extends Plugin {
|
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
start(framework) {
|
|
43
|
-
// 创建子Agent
|
|
43
|
+
// 创建子Agent(可能需要等待父agent准备好)
|
|
44
44
|
this._createSubAgent()
|
|
45
45
|
|
|
46
46
|
// 注册委托工具到主Agent(通过framework获取父agent)
|
|
@@ -56,7 +56,30 @@ class SubAgentPlugin extends Plugin {
|
|
|
56
56
|
// 获取父Agent(主Agent)
|
|
57
57
|
const parentAgent = this._getParentAgent()
|
|
58
58
|
if (!parentAgent) {
|
|
59
|
-
|
|
59
|
+
// 父agent还没创建,监听事件等其创建
|
|
60
|
+
if (this._framework) {
|
|
61
|
+
const framework = this._framework
|
|
62
|
+
framework.on('agent:created', (agent) => {
|
|
63
|
+
if (framework._mainAgent && agent === framework._mainAgent) {
|
|
64
|
+
// 父agent已创建,重新尝试创建子agent
|
|
65
|
+
this._doCreateSubAgent(agent)
|
|
66
|
+
// 注册委托工具
|
|
67
|
+
this._registerDelegateTool()
|
|
68
|
+
}
|
|
69
|
+
})
|
|
70
|
+
}
|
|
71
|
+
return
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
this._doCreateSubAgent(parentAgent)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* 实际执行创建子Agent
|
|
79
|
+
*/
|
|
80
|
+
_doCreateSubAgent(parentAgent) {
|
|
81
|
+
// 避免重复创建
|
|
82
|
+
if (this._agent) {
|
|
60
83
|
return
|
|
61
84
|
}
|
|
62
85
|
|
|
@@ -186,27 +209,32 @@ class SubAgentPlugin extends Plugin {
|
|
|
186
209
|
}
|
|
187
210
|
|
|
188
211
|
/**
|
|
189
|
-
*
|
|
212
|
+
* 注册委托工具到框架(会被同步到主Agent)
|
|
190
213
|
*/
|
|
191
214
|
_registerDelegateTool() {
|
|
192
|
-
const
|
|
193
|
-
if (!
|
|
215
|
+
const framework = this._framework
|
|
216
|
+
if (!framework) return
|
|
194
217
|
|
|
195
218
|
const agentName = this.config.name
|
|
196
219
|
const role = this.config.role
|
|
197
220
|
|
|
198
|
-
//
|
|
199
|
-
|
|
221
|
+
// 注册工具到框架工具注册表
|
|
222
|
+
framework.registerTool({
|
|
200
223
|
name: agentName,
|
|
201
224
|
description: `${role}:当需要${role}时,调用此工具执行任务。如:编译代码、运行测试、代码重构等。`,
|
|
202
225
|
inputSchema: z.object({
|
|
203
226
|
task: z.string().describe('给子Agent的具体任务描述')
|
|
204
227
|
}),
|
|
205
|
-
execute: async (args,
|
|
228
|
+
execute: async (args, fw) => {
|
|
206
229
|
if (!this._agent) {
|
|
207
230
|
return { error: `SubAgent ${agentName} not initialized` }
|
|
208
231
|
}
|
|
209
232
|
|
|
233
|
+
const parentAgent = fw._mainAgent
|
|
234
|
+
if (!parentAgent) {
|
|
235
|
+
return { error: `Parent agent not found` }
|
|
236
|
+
}
|
|
237
|
+
|
|
210
238
|
// 发射子Agent开始处理事件
|
|
211
239
|
parentAgent.emit('subagent:chat:start', {
|
|
212
240
|
parentAgent,
|
|
@@ -323,11 +351,9 @@ class SubAgentManagerPlugin extends Plugin {
|
|
|
323
351
|
this._subAgents.set(agentConfig.name, plugin)
|
|
324
352
|
}
|
|
325
353
|
|
|
326
|
-
//
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
this._registerToolsToAgent(mainAgent)
|
|
330
|
-
}
|
|
354
|
+
// 注册管理工具到框架工具注册表
|
|
355
|
+
// 这样当 agent 创建时会通过 _syncTools() 自动同步过来
|
|
356
|
+
this._registerTools(framework)
|
|
331
357
|
|
|
332
358
|
// 启动文件监控
|
|
333
359
|
this._startFileWatcher()
|
|
@@ -336,12 +362,10 @@ class SubAgentManagerPlugin extends Plugin {
|
|
|
336
362
|
}
|
|
337
363
|
|
|
338
364
|
/**
|
|
339
|
-
*
|
|
365
|
+
* 注册管理工具到框架工具注册表
|
|
340
366
|
*/
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
agent.registerTool({
|
|
367
|
+
_registerTools(framework) {
|
|
368
|
+
framework.registerTool({
|
|
345
369
|
name: 'subagent_list',
|
|
346
370
|
description: '列出所有子Agent',
|
|
347
371
|
inputSchema: z.object({}),
|
|
@@ -356,7 +380,7 @@ class SubAgentManagerPlugin extends Plugin {
|
|
|
356
380
|
}
|
|
357
381
|
})
|
|
358
382
|
|
|
359
|
-
|
|
383
|
+
framework.registerTool({
|
|
360
384
|
name: 'subagent_call',
|
|
361
385
|
description: '调用指定的子Agent处理任务',
|
|
362
386
|
inputSchema: z.object({
|
|
@@ -383,7 +407,7 @@ class SubAgentManagerPlugin extends Plugin {
|
|
|
383
407
|
}
|
|
384
408
|
})
|
|
385
409
|
|
|
386
|
-
|
|
410
|
+
framework.registerTool({
|
|
387
411
|
name: 'subagent_reload',
|
|
388
412
|
description: '重新加载子Agent配置(当新增或删除.agent/agents下的文件后使用)',
|
|
389
413
|
inputSchema: z.object({}),
|
|
@@ -397,7 +421,7 @@ class SubAgentManagerPlugin extends Plugin {
|
|
|
397
421
|
}
|
|
398
422
|
})
|
|
399
423
|
|
|
400
|
-
console.log('[SubAgentManager] Management tools registered to
|
|
424
|
+
console.log('[SubAgentManager] Management tools registered to framework')
|
|
401
425
|
}
|
|
402
426
|
|
|
403
427
|
/**
|
|
@@ -405,11 +429,14 @@ class SubAgentManagerPlugin extends Plugin {
|
|
|
405
429
|
*/
|
|
406
430
|
_loadAgentsFromDir() {
|
|
407
431
|
const agentsDir = this.config.agentsDir
|
|
432
|
+
console.log('[SubAgentManager] _loadAgentsFromDir called, agentsDir:', agentsDir)
|
|
408
433
|
if (!agentsDir || !fs.existsSync(agentsDir)) {
|
|
434
|
+
console.log('[SubAgentManager] agentsDir not found or does not exist')
|
|
409
435
|
return
|
|
410
436
|
}
|
|
411
437
|
|
|
412
438
|
const entries = fs.readdirSync(agentsDir, { withFileTypes: true })
|
|
439
|
+
console.log('[SubAgentManager] Found entries:', entries.length)
|
|
413
440
|
|
|
414
441
|
for (const entry of entries) {
|
|
415
442
|
if (entry.isFile() && (entry.name.endsWith('.js') || entry.name.endsWith('.json') || entry.name.endsWith('.md'))) {
|
|
@@ -429,6 +456,7 @@ class SubAgentManagerPlugin extends Plugin {
|
|
|
429
456
|
} else if (entry.name.endsWith('.md')) {
|
|
430
457
|
// 解析 markdown 文件,提取 JSON 配置
|
|
431
458
|
agentConfig = this._parseMarkdownConfig(filePath, baseName)
|
|
459
|
+
console.log('[SubAgentManager] Parsed md:', baseName, agentConfig)
|
|
432
460
|
} else {
|
|
433
461
|
// 清除缓存并加载
|
|
434
462
|
delete require.cache[require.resolve(filePath)]
|
|
@@ -439,9 +467,12 @@ class SubAgentManagerPlugin extends Plugin {
|
|
|
439
467
|
if (agentConfig && agentConfig.name) {
|
|
440
468
|
agentConfig._fromDir = true
|
|
441
469
|
this.config.agents.push(agentConfig)
|
|
470
|
+
console.log('[SubAgentManager] Loaded agent:', agentConfig.name)
|
|
471
|
+
} else {
|
|
472
|
+
console.warn('[SubAgentManager] Agent config has no name:', baseName, agentConfig)
|
|
442
473
|
}
|
|
443
474
|
} catch (err) {
|
|
444
|
-
console.warn(`[SubAgentManager] Failed to load agent config from ${filePath}:`, err.message)
|
|
475
|
+
console.warn(`[SubAgentManager] Failed to load agent config from ${filePath}:`, err.message, err.stack)
|
|
445
476
|
}
|
|
446
477
|
}
|
|
447
478
|
}
|
|
@@ -457,48 +488,34 @@ class SubAgentManagerPlugin extends Plugin {
|
|
|
457
488
|
*/
|
|
458
489
|
_parseMarkdownConfig(filePath, defaultName) {
|
|
459
490
|
const content = fs.readFileSync(filePath, 'utf-8')
|
|
491
|
+
console.log('[SubAgentManager] _parseMarkdownConfig:', filePath)
|
|
460
492
|
|
|
461
493
|
// 尝试从 code block 中提取 JSON
|
|
462
494
|
const jsonMatch = content.match(/```(?:json)?\s*\n([\s\S]*?)\n\s*```/)
|
|
463
495
|
if (jsonMatch) {
|
|
464
496
|
try {
|
|
465
497
|
const config = JSON.parse(jsonMatch[1].trim())
|
|
498
|
+
console.log('[SubAgentManager] Found JSON in code block')
|
|
466
499
|
return config
|
|
467
500
|
} catch (err) {
|
|
468
501
|
// JSON 解析失败,继续
|
|
469
502
|
}
|
|
470
503
|
}
|
|
471
504
|
|
|
472
|
-
// 尝试从
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
if (line.trim() === '---') {
|
|
482
|
-
if (inFrontmatter) {
|
|
483
|
-
// 解析 frontmatter
|
|
484
|
-
try {
|
|
485
|
-
const parsed = this._parseYamlLike(frontmatterContent)
|
|
486
|
-
return { ...config, ...parsed }
|
|
487
|
-
} catch (err) {
|
|
488
|
-
// 解析失败
|
|
489
|
-
}
|
|
490
|
-
}
|
|
491
|
-
inFrontmatter = true
|
|
492
|
-
frontmatterContent = ''
|
|
493
|
-
continue
|
|
494
|
-
}
|
|
495
|
-
|
|
496
|
-
if (inFrontmatter) {
|
|
497
|
-
frontmatterContent += line + '\n'
|
|
505
|
+
// 尝试从 frontmatter 格式中提取
|
|
506
|
+
const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/)
|
|
507
|
+
if (frontmatterMatch) {
|
|
508
|
+
console.log('[SubAgentManager] Found frontmatter')
|
|
509
|
+
const frontmatterContent = frontmatterMatch[1]
|
|
510
|
+
const parsed = this._parseYamlLike(frontmatterContent)
|
|
511
|
+
if (parsed && parsed.name) {
|
|
512
|
+
console.log('[SubAgentManager] Parsed frontmatter:', parsed)
|
|
513
|
+
return { name: parsed.name || defaultName, ...parsed }
|
|
498
514
|
}
|
|
499
515
|
}
|
|
500
516
|
|
|
501
517
|
// 从 markdown 内容中提取配置(key: value 格式)
|
|
518
|
+
const config = { name: defaultName }
|
|
502
519
|
const yamlMatch = content.match(/^(name|role|description|parentTools|tools):\s*(.+)$/m)
|
|
503
520
|
if (yamlMatch) {
|
|
504
521
|
config[yamlMatch[1]] = yamlMatch[2].trim()
|
|
@@ -27,6 +27,8 @@ module.exports = function(Plugin) {
|
|
|
27
27
|
this.version = '2.0.0'
|
|
28
28
|
this.description = 'Telegram 对话插件,绑定主Agent进行持续对话'
|
|
29
29
|
this.priority = 80
|
|
30
|
+
// 默认不启用,需要在 plugins.json 中设置 enabled: true
|
|
31
|
+
this.enabled = false
|
|
30
32
|
|
|
31
33
|
this.config = {
|
|
32
34
|
botToken: config.botToken || process.env.TELEGRAM_BOT_TOKEN,
|
package/plugins/tools-plugin.js
CHANGED
|
@@ -61,18 +61,20 @@ class ToolsPlugin extends Plugin {
|
|
|
61
61
|
}
|
|
62
62
|
})
|
|
63
63
|
|
|
64
|
-
//
|
|
64
|
+
// 列出所有插件工具(包括未加载的)
|
|
65
65
|
framework.registerTool({
|
|
66
66
|
name: 'list_plugins',
|
|
67
|
-
description: '
|
|
67
|
+
description: '列出所有插件(包括未加载的)',
|
|
68
68
|
inputSchema: z.object({}),
|
|
69
69
|
execute: async () => {
|
|
70
|
-
const plugins = framework.pluginManager.
|
|
70
|
+
const plugins = framework.pluginManager.getAllKnown()
|
|
71
71
|
return {
|
|
72
72
|
success: true,
|
|
73
73
|
plugins: plugins.map(p => ({
|
|
74
74
|
name: p.name,
|
|
75
|
-
|
|
75
|
+
status: p.status,
|
|
76
|
+
enabled: p.enabled,
|
|
77
|
+
version: p.version
|
|
76
78
|
}))
|
|
77
79
|
}
|
|
78
80
|
}
|
package/plugins/weixin-plugin.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* WeChat 插件
|
|
3
|
-
*
|
|
3
|
+
* 使用 @chnak/weixin-bot 实现微信对话
|
|
4
4
|
*
|
|
5
5
|
* 配置:
|
|
6
6
|
* - forceLogin: 是否强制重新扫码登录
|
|
@@ -18,6 +18,8 @@ module.exports = function(Plugin) {
|
|
|
18
18
|
this.version = '1.0.0'
|
|
19
19
|
this.description = '微信对话插件,使用微信网页账号进行对话'
|
|
20
20
|
this.priority = 80
|
|
21
|
+
// 默认不启用,需要在 plugins.json 中设置 enabled: true
|
|
22
|
+
this.enabled = false
|
|
21
23
|
|
|
22
24
|
this.config = {
|
|
23
25
|
forceLogin: config.forceLogin || process.env.WEIXIN_FORCE_LOGIN === 'true',
|
|
@@ -73,8 +75,8 @@ module.exports = function(Plugin) {
|
|
|
73
75
|
const module = await import('@chnak/weixin-bot')
|
|
74
76
|
WeixinBot = module.WeixinBot
|
|
75
77
|
} catch (err) {
|
|
76
|
-
console.warn('[WeChat] Failed to load@chnak/weixin-bot:', err.message)
|
|
77
|
-
console.warn('[WeChat] Make sure@chnak/weixin-bot is installed: npm install@chnak/weixin-bot qrcode-terminal')
|
|
78
|
+
console.warn('[WeChat] Failed to load @chnak/weixin-bot:', err.message)
|
|
79
|
+
console.warn('[WeChat] Make sure @chnak/weixin-bot is installed: npm install @chnak/weixin-bot qrcode-terminal')
|
|
78
80
|
return
|
|
79
81
|
}
|
|
80
82
|
|
package/src/core/plugin-base.js
CHANGED
|
@@ -16,6 +16,7 @@ class PluginManager {
|
|
|
16
16
|
this._plugins = new Map()
|
|
17
17
|
this._loading = false
|
|
18
18
|
this._stateFile = path.join(process.cwd(), '.agent', 'data', 'plugins-state.json')
|
|
19
|
+
this._knownPlugins = new Set() // 已知插件列表(包括未加载的)
|
|
19
20
|
}
|
|
20
21
|
|
|
21
22
|
/**
|
|
@@ -89,10 +90,13 @@ class PluginManager {
|
|
|
89
90
|
pluginInstance.config = { ...pluginInstance.config, ...savedConfig }
|
|
90
91
|
}
|
|
91
92
|
|
|
93
|
+
// 使用保存的状态,否则使用插件实例的 enabled,默认 true
|
|
94
|
+
const finalEnabled = savedEnabled !== undefined ? savedEnabled : (pluginInstance.enabled !== undefined ? pluginInstance.enabled : true)
|
|
95
|
+
|
|
92
96
|
this._plugins.set(pluginInstance.name, {
|
|
93
97
|
instance: pluginInstance,
|
|
94
98
|
status: 'registered',
|
|
95
|
-
enabled:
|
|
99
|
+
enabled: finalEnabled
|
|
96
100
|
})
|
|
97
101
|
|
|
98
102
|
this.framework.emit('plugin:registered', pluginInstance)
|
|
@@ -116,7 +120,7 @@ class PluginManager {
|
|
|
116
120
|
pluginInstance = this._createFromObject(plugin)
|
|
117
121
|
}
|
|
118
122
|
|
|
119
|
-
//
|
|
123
|
+
// 如果已注册,使用已注册的实例和状态
|
|
120
124
|
const existing = this._plugins.get(pluginInstance.name)
|
|
121
125
|
if (existing) {
|
|
122
126
|
pluginInstance = existing.instance
|
|
@@ -424,12 +428,59 @@ class PluginManager {
|
|
|
424
428
|
.map(e => ({ name: e.instance.name, instance: e.instance }))
|
|
425
429
|
}
|
|
426
430
|
|
|
431
|
+
/**
|
|
432
|
+
* 注册一个已知插件(但不加载)
|
|
433
|
+
* 用于显示所有可用插件列表
|
|
434
|
+
* @param {string} name - 插件名称
|
|
435
|
+
* @param {Object} info - 插件信息
|
|
436
|
+
*/
|
|
437
|
+
registerKnownPlugin(name, info = {}) {
|
|
438
|
+
this._knownPlugins.add(name)
|
|
439
|
+
// 如果插件还没注册过,记录它的信息
|
|
440
|
+
if (!this._plugins.has(name)) {
|
|
441
|
+
this._plugins.set(name, {
|
|
442
|
+
instance: { name, ...info },
|
|
443
|
+
status: 'known',
|
|
444
|
+
enabled: info.enabled !== undefined ? info.enabled : false
|
|
445
|
+
})
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
/**
|
|
450
|
+
* 获取所有已知插件(包括未加载的)
|
|
451
|
+
* @returns {Array} 插件列表,包含 name, status, enabled
|
|
452
|
+
*/
|
|
453
|
+
getAllKnown() {
|
|
454
|
+
const result = []
|
|
455
|
+
for (const name of this._knownPlugins) {
|
|
456
|
+
const entry = this._plugins.get(name)
|
|
457
|
+
result.push({
|
|
458
|
+
name,
|
|
459
|
+
status: entry?.status || 'unknown',
|
|
460
|
+
enabled: entry?.enabled || false,
|
|
461
|
+
version: entry?.instance?.version
|
|
462
|
+
})
|
|
463
|
+
}
|
|
464
|
+
// 也加入已加载但不在 knownPlugins 中的
|
|
465
|
+
for (const [name, entry] of this._plugins) {
|
|
466
|
+
if (!result.find(p => p.name === name)) {
|
|
467
|
+
result.push({
|
|
468
|
+
name,
|
|
469
|
+
status: entry.status,
|
|
470
|
+
enabled: entry.enabled,
|
|
471
|
+
version: entry.instance?.version
|
|
472
|
+
})
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
return result
|
|
476
|
+
}
|
|
477
|
+
|
|
427
478
|
/**
|
|
428
479
|
* 检查插件是否存在
|
|
429
480
|
* @param {string} name - 插件名称
|
|
430
481
|
*/
|
|
431
482
|
has(name) {
|
|
432
|
-
return this._plugins.has(name)
|
|
483
|
+
return this._plugins.has(name) || this._knownPlugins.has(name)
|
|
433
484
|
}
|
|
434
485
|
|
|
435
486
|
/**
|
|
@@ -464,8 +515,12 @@ class PluginManager {
|
|
|
464
515
|
}
|
|
465
516
|
|
|
466
517
|
entry.enabled = true
|
|
518
|
+
// 同步更新插件实例的 enabled 属性,避免 load() 时被跳过
|
|
519
|
+
if (entry.instance) {
|
|
520
|
+
entry.instance.enabled = true
|
|
521
|
+
}
|
|
467
522
|
|
|
468
|
-
//
|
|
523
|
+
// 如果插件已加载,尝试重新启动
|
|
469
524
|
if (entry.status === 'loaded') {
|
|
470
525
|
try {
|
|
471
526
|
// 如果之前已经启动过,先调用 stop 停止旧实例
|
|
@@ -481,6 +536,13 @@ class PluginManager {
|
|
|
481
536
|
} catch (err) {
|
|
482
537
|
console.error(`[PluginManager] Enable/reload failed for '${name}':`, err.message)
|
|
483
538
|
}
|
|
539
|
+
} else if (entry.status === 'registered' || entry.status === 'known') {
|
|
540
|
+
// 插件只注册过但未加载,现在加载它
|
|
541
|
+
try {
|
|
542
|
+
await this.load(entry.instance)
|
|
543
|
+
} catch (err) {
|
|
544
|
+
console.error(`[PluginManager] Enable/load failed for '${name}':`, err.message)
|
|
545
|
+
}
|
|
484
546
|
}
|
|
485
547
|
|
|
486
548
|
this.framework.emit('plugin:enabled', entry.instance)
|
|
@@ -504,6 +566,10 @@ class PluginManager {
|
|
|
504
566
|
}
|
|
505
567
|
|
|
506
568
|
entry.enabled = false
|
|
569
|
+
// 同步更新插件实例的 enabled 属性
|
|
570
|
+
if (entry.instance) {
|
|
571
|
+
entry.instance.enabled = false
|
|
572
|
+
}
|
|
507
573
|
|
|
508
574
|
// 如果插件正在运行,停止它
|
|
509
575
|
if (entry.instance._started) {
|