foliko 1.0.0 → 1.0.2
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 +3 -1
- package/README.md +132 -172
- package/cli/src/ui/chat-ui.js +27 -2
- package/install.ps1 +179 -0
- package/package.json +2 -2
- package/plugins/default-plugins.js +9 -0
- package/plugins/python-executor-plugin.js +3 -3
- package/plugins/python-plugin-loader.js +478 -0
- package/skills/python-plugin-dev/SKILL.md +265 -0
- package/website/docs/api.html +159 -0
- package/website/docs/configuration.html +119 -0
- package/website/docs/plugin-development.html +155 -0
- package/website/docs/project-structure.html +142 -0
- package/website/docs/skill-development.html +85 -0
- package/website/index.html +197 -0
- package/website/script.js +77 -0
- package/website/styles.css +306 -0
|
@@ -24,7 +24,9 @@
|
|
|
24
24
|
"Bash(cd D:/Code/vb-agent && node test-stream-emoji.js 2>&1)",
|
|
25
25
|
"Read(//d/Date/20260321/app/**)",
|
|
26
26
|
"Bash(node -e \":*)",
|
|
27
|
-
"Bash(node -e \"\nconst { loadAgentConfig } = require\\('./plugins/default-plugins'\\);\nconst config = loadAgentConfig\\('D:/Date/20260321/app/.agent'\\);\nconsole.log\\('skillsDirs:', config.skillsDirs\\);\n\")"
|
|
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
|
+
"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)"
|
|
28
30
|
]
|
|
29
31
|
}
|
|
30
32
|
}
|
package/README.md
CHANGED
|
@@ -1,217 +1,177 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Foliko
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
简约的插件化 Agent 框架
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## 特性
|
|
6
6
|
|
|
7
|
-
-
|
|
8
|
-
-
|
|
9
|
-
-
|
|
7
|
+
- **插件化架构** - 核心简单,通过插件扩展功能
|
|
8
|
+
- **流式输出** - 支持实时流式输出
|
|
9
|
+
- **多 AI 支持** - 支持 Anthropic、DeepSeek、MiniMax 等
|
|
10
|
+
- **内置工具** - Shell、Python、MCP、文件系统等
|
|
11
|
+
- **技能系统** - 可扩展的 Skill 管理
|
|
12
|
+
- **会话管理** - 支持多会话切换
|
|
13
|
+
- **规则引擎** - 可配置的行为规则
|
|
10
14
|
|
|
11
|
-
##
|
|
15
|
+
## 快速开始
|
|
12
16
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
- 边界情况测试
|
|
17
|
-
- 异常测试
|
|
18
|
-
|
|
19
|
-
### 2. 异步测试
|
|
20
|
-
- Promise 测试
|
|
21
|
-
- async/await 测试
|
|
22
|
-
- 超时处理
|
|
23
|
-
|
|
24
|
-
### 3. 集成测试
|
|
25
|
-
- 多个类的组合测试
|
|
26
|
-
- 完整流程测试
|
|
27
|
-
|
|
28
|
-
### 4. Mock 和 Spy
|
|
29
|
-
- 模拟函数
|
|
30
|
-
- 模拟 API 调用
|
|
31
|
-
- 验证函数调用
|
|
32
|
-
|
|
33
|
-
### 5. 参数化测试
|
|
34
|
-
- 使用 `test.each` 测试多组数据
|
|
35
|
-
|
|
36
|
-
### 6. 快照测试
|
|
37
|
-
- 对象结构验证
|
|
38
|
-
|
|
39
|
-
## 最佳实践展示
|
|
17
|
+
```bash
|
|
18
|
+
# 安装
|
|
19
|
+
npm install
|
|
40
20
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
test('应该正确计算两个数的和', () => {
|
|
44
|
-
// Arrange: 准备测试数据
|
|
45
|
-
const a = 5;
|
|
46
|
-
const b = 3;
|
|
47
|
-
const expected = 8;
|
|
48
|
-
|
|
49
|
-
// Act: 执行被测试的方法
|
|
50
|
-
const result = calculator.add(a, b);
|
|
51
|
-
|
|
52
|
-
// Assert: 验证结果
|
|
53
|
-
expect(result).toBe(expected);
|
|
54
|
-
});
|
|
21
|
+
# 启动聊天
|
|
22
|
+
npm run chat
|
|
55
23
|
```
|
|
56
24
|
|
|
57
|
-
|
|
58
|
-
- 使用 "should" 描述预期行为
|
|
59
|
-
- 包含测试条件
|
|
60
|
-
- 清晰表达测试目的
|
|
61
|
-
|
|
62
|
-
### 3. 测试隔离
|
|
63
|
-
- 每个测试独立运行
|
|
64
|
-
- 使用 `beforeEach` 重置状态
|
|
65
|
-
- 避免测试间的依赖
|
|
25
|
+
## 项目结构
|
|
66
26
|
|
|
67
|
-
|
|
68
|
-
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
## 安装和运行
|
|
79
|
-
|
|
80
|
-
### 1. 安装依赖
|
|
81
|
-
```bash
|
|
82
|
-
npm install
|
|
27
|
+
```
|
|
28
|
+
vb-agent/
|
|
29
|
+
├── cli/ # 命令行入口
|
|
30
|
+
│ └── bin/foliko.js # CLI 入口
|
|
31
|
+
├── src/ # 核心框架
|
|
32
|
+
│ ├── core/ # 核心组件
|
|
33
|
+
│ ├── capabilities/ # 能力插件
|
|
34
|
+
│ └── executors/ # 执行器
|
|
35
|
+
├── plugins/ # 内置插件
|
|
36
|
+
├── skills/ # 技能目录
|
|
37
|
+
└── examples/ # 示例
|
|
83
38
|
```
|
|
84
39
|
|
|
85
|
-
|
|
86
|
-
```bash
|
|
87
|
-
# 运行所有测试
|
|
88
|
-
npm test
|
|
40
|
+
## 配置
|
|
89
41
|
|
|
90
|
-
|
|
91
|
-
npm run test:watch
|
|
42
|
+
在项目根目录创建 `.agent` 目录:
|
|
92
43
|
|
|
93
|
-
|
|
94
|
-
|
|
44
|
+
```
|
|
45
|
+
.agent/
|
|
46
|
+
├── config # 配置文件
|
|
47
|
+
├── ai.json # AI 配置
|
|
48
|
+
├── mcp_config.json # MCP 服务器配置
|
|
49
|
+
├── plugins/ # 用户插件目录
|
|
50
|
+
├── skills/ # 用户技能目录
|
|
51
|
+
└── data/ # 数据目录
|
|
52
|
+
```
|
|
95
53
|
|
|
96
|
-
|
|
97
|
-
npm run test:verbose
|
|
54
|
+
### config 文件格式
|
|
98
55
|
|
|
99
|
-
|
|
100
|
-
|
|
56
|
+
```
|
|
57
|
+
ai_key: your-api-key
|
|
58
|
+
ai_model: MiniMax-M2.7
|
|
59
|
+
ai_provider: minimax
|
|
101
60
|
```
|
|
102
61
|
|
|
103
|
-
###
|
|
104
|
-
运行 `npm run test:coverage` 后,打开 `coverage/index.html` 查看详细的覆盖率报告。
|
|
62
|
+
### ai.json 格式
|
|
105
63
|
|
|
106
|
-
|
|
64
|
+
```json
|
|
65
|
+
{
|
|
66
|
+
"provider": "minimax",
|
|
67
|
+
"model": "MiniMax-M2.7",
|
|
68
|
+
"apiKey": "your-api-key",
|
|
69
|
+
"baseURL": "https://api.minimaxi.com/v1"
|
|
70
|
+
}
|
|
71
|
+
```
|
|
107
72
|
|
|
108
|
-
|
|
73
|
+
### mcp_config.json 格式
|
|
109
74
|
|
|
110
75
|
```json
|
|
111
76
|
{
|
|
112
|
-
"
|
|
113
|
-
"
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
"coverageReporters": ["text", "lcov", "html"], // 报告格式
|
|
118
|
-
"coverageThreshold": { // 覆盖率阈值
|
|
119
|
-
"global": {
|
|
120
|
-
"branches": 80,
|
|
121
|
-
"functions": 80,
|
|
122
|
-
"lines": 80,
|
|
123
|
-
"statements": 80
|
|
124
|
-
}
|
|
125
|
-
},
|
|
126
|
-
"testMatch": [ // 测试文件匹配模式
|
|
127
|
-
"**/__tests__/**/*.js",
|
|
128
|
-
"**/?(*.)+(spec|test).js"
|
|
129
|
-
],
|
|
130
|
-
"testTimeout": 10000, // 测试超时时间
|
|
131
|
-
"clearMocks": true, // 自动清理 mock
|
|
132
|
-
"resetMocks": true, // 重置 mock
|
|
133
|
-
"restoreMocks": true // 恢复原始实现
|
|
77
|
+
"mcpServers": {
|
|
78
|
+
"fetch": {
|
|
79
|
+
"command": "npx",
|
|
80
|
+
"args": ["-y", "@modelcontextprotocol/server-fetch"]
|
|
81
|
+
}
|
|
134
82
|
}
|
|
135
83
|
}
|
|
136
84
|
```
|
|
137
85
|
|
|
138
|
-
##
|
|
86
|
+
## 开发插件
|
|
87
|
+
|
|
88
|
+
### 用户插件(.agent/plugins/)
|
|
139
89
|
|
|
140
|
-
### 1. 测试异步代码
|
|
141
90
|
```javascript
|
|
142
|
-
//
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
}
|
|
91
|
+
// .agent/plugins/my-plugin.js
|
|
92
|
+
module.exports = function(Plugin) {
|
|
93
|
+
return class MyPlugin extends Plugin {
|
|
94
|
+
constructor(config = {}) {
|
|
95
|
+
super()
|
|
96
|
+
this.name = 'my-plugin'
|
|
97
|
+
this.version = '1.0.0'
|
|
98
|
+
this.description = '我的工具插件'
|
|
99
|
+
this.priority = 10
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
install(framework) {
|
|
103
|
+
const { z } = require('zod')
|
|
104
|
+
framework.registerTool({
|
|
105
|
+
name: 'my_tool',
|
|
106
|
+
description: '我的工具',
|
|
107
|
+
inputSchema: z.object({
|
|
108
|
+
param: z.string().describe('参数描述')
|
|
109
|
+
}),
|
|
110
|
+
execute: async (args, framework) => {
|
|
111
|
+
return { success: true, result: args.param }
|
|
112
|
+
}
|
|
113
|
+
})
|
|
114
|
+
return this
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
152
118
|
```
|
|
153
119
|
|
|
154
|
-
|
|
155
|
-
```javascript
|
|
156
|
-
// 创建 mock 函数
|
|
157
|
-
const mockFn = jest.fn();
|
|
120
|
+
**注意**:如果插件需要第三方库(如 `zod`),需要先安装:
|
|
158
121
|
|
|
159
|
-
|
|
160
|
-
|
|
122
|
+
1. 创建插件文件
|
|
123
|
+
2. 调用 `install` 工具安装依赖
|
|
124
|
+
3. 热重载插件
|
|
161
125
|
|
|
162
|
-
|
|
163
|
-
expect(mockFn).toHaveBeenCalledTimes(1);
|
|
164
|
-
expect(mockFn).toHaveBeenCalledWith('参数');
|
|
165
|
-
```
|
|
126
|
+
### 技能开发
|
|
166
127
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
[2, 1, 3],
|
|
173
|
-
])('add(%i, %i) should equal %i', (a, b, expected) => {
|
|
174
|
-
expect(a + b).toBe(expected);
|
|
175
|
-
});
|
|
128
|
+
在 `.agent/skills/` 下创建技能:
|
|
129
|
+
|
|
130
|
+
```
|
|
131
|
+
.agent/skills/my-skill/
|
|
132
|
+
└── SKILL.md
|
|
176
133
|
```
|
|
177
134
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
date: expect.any(Date) // 忽略具体日期值
|
|
189
|
-
});
|
|
190
|
-
});
|
|
135
|
+
SKILL.md 格式:
|
|
136
|
+
|
|
137
|
+
```markdown
|
|
138
|
+
---
|
|
139
|
+
name: my-skill
|
|
140
|
+
description: 技能描述
|
|
141
|
+
allowed-tools: tool1,tool2
|
|
142
|
+
---
|
|
143
|
+
|
|
144
|
+
技能内容...
|
|
191
145
|
```
|
|
192
146
|
|
|
193
|
-
##
|
|
147
|
+
## CLI 命令
|
|
194
148
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
149
|
+
```bash
|
|
150
|
+
# 聊天模式
|
|
151
|
+
foliko chat
|
|
152
|
+
|
|
153
|
+
# 指定模型
|
|
154
|
+
foliko chat --model gpt-4
|
|
155
|
+
|
|
156
|
+
# 指定 API
|
|
157
|
+
foliko chat --api-key xxx
|
|
158
|
+
```
|
|
199
159
|
|
|
200
|
-
|
|
201
|
-
- 确保测试独立
|
|
202
|
-
- 避免依赖外部状态
|
|
203
|
-
- 使用固定的测试数据
|
|
160
|
+
## 内置工具
|
|
204
161
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
162
|
+
| 工具 | 说明 |
|
|
163
|
+
|------|------|
|
|
164
|
+
| `loadSkill` | 加载技能 |
|
|
165
|
+
| `mcp_execute` | 执行 MCP 工具 |
|
|
166
|
+
| `shell_execute` | 执行 Shell 命令 |
|
|
167
|
+
| `python_execute` | 执行 Python 代码 |
|
|
168
|
+
| `install` | 安装 npm 包 |
|
|
169
|
+
| `mcp_reload` | 重载 MCP 服务器 |
|
|
170
|
+
| `audit_query` | 查询审计日志 |
|
|
209
171
|
|
|
210
|
-
##
|
|
172
|
+
## 工作目录
|
|
211
173
|
|
|
212
|
-
|
|
213
|
-
- [测试金字塔原则](https://martinfowler.com/bliki/TestPyramid.html)
|
|
214
|
-
- [AAA 测试模式](https://medium.com/@pjbgf/title-testing-code-ocd-and-the-aaa-pattern-df453975ab80)
|
|
174
|
+
CLI 工作目录默认使用执行命令的目录。
|
|
215
175
|
|
|
216
176
|
## 许可证
|
|
217
177
|
|
package/cli/src/ui/chat-ui.js
CHANGED
|
@@ -208,6 +208,20 @@ class ChatUI extends EventEmitter {
|
|
|
208
208
|
console.log(colored('Agent:', GREEN))
|
|
209
209
|
console.log()
|
|
210
210
|
|
|
211
|
+
// 用于打断的标志
|
|
212
|
+
let interrupted = false
|
|
213
|
+
|
|
214
|
+
// 设置打断处理
|
|
215
|
+
const interruptHandler = () => {
|
|
216
|
+
if (!interrupted) {
|
|
217
|
+
interrupted = true
|
|
218
|
+
console.log(`\n${colored('[中断]', RED)} 输出已中断`)
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// 监听 Ctrl+C
|
|
223
|
+
process.on('SIGINT', interruptHandler)
|
|
224
|
+
|
|
211
225
|
try {
|
|
212
226
|
let lineBuffer = ''
|
|
213
227
|
// 渲染状态追踪
|
|
@@ -233,6 +247,9 @@ class ChatUI extends EventEmitter {
|
|
|
233
247
|
}
|
|
234
248
|
|
|
235
249
|
for await (const chunk of this.agent.chatStream(message)) {
|
|
250
|
+
// 检查是否被打断
|
|
251
|
+
if (interrupted) break
|
|
252
|
+
|
|
236
253
|
if (chunk.type === 'text') {
|
|
237
254
|
lineBuffer += chunk.text
|
|
238
255
|
|
|
@@ -243,6 +260,9 @@ class ChatUI extends EventEmitter {
|
|
|
243
260
|
|
|
244
261
|
// 当有一行完整内容时,渲染并输出
|
|
245
262
|
while (lineBuffer.includes('\n')) {
|
|
263
|
+
// 检查是否被打断
|
|
264
|
+
if (interrupted) break
|
|
265
|
+
|
|
246
266
|
const nlIndex = lineBuffer.indexOf('\n')
|
|
247
267
|
const line = lineBuffer.substring(0, nlIndex)
|
|
248
268
|
lineBuffer = lineBuffer.substring(nlIndex + 1)
|
|
@@ -258,11 +278,16 @@ class ChatUI extends EventEmitter {
|
|
|
258
278
|
}
|
|
259
279
|
|
|
260
280
|
// 输出剩余内容
|
|
261
|
-
if (lineBuffer.trim()) {
|
|
281
|
+
if (lineBuffer.trim() && !interrupted) {
|
|
262
282
|
console.log(renderLine(lineBuffer, renderState))
|
|
263
283
|
}
|
|
264
284
|
} catch (err) {
|
|
265
|
-
|
|
285
|
+
if (!interrupted) {
|
|
286
|
+
console.error(`\n${colored('[错误]', RED)} ${err.message}\n`)
|
|
287
|
+
}
|
|
288
|
+
} finally {
|
|
289
|
+
// 移除监听器
|
|
290
|
+
process.removeListener('SIGINT', interruptHandler)
|
|
266
291
|
}
|
|
267
292
|
|
|
268
293
|
this.prompt()
|
package/install.ps1
ADDED
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
# Foliko 安装脚本
|
|
2
|
+
# 使用方式: irm https://raw.githubusercontent.com/user/vb-agent/main/install.ps1 | iex
|
|
3
|
+
|
|
4
|
+
$ErrorActionPreference = "Stop"
|
|
5
|
+
|
|
6
|
+
$Repo = "your-username/vb-agent"
|
|
7
|
+
$InstallDir = "$env:LOCALAPPDATA\Foliko"
|
|
8
|
+
|
|
9
|
+
Write-Host "Foliko 安装器" -ForegroundColor Cyan
|
|
10
|
+
Write-Host "=================" -ForegroundColor Cyan
|
|
11
|
+
Write-Host ""
|
|
12
|
+
|
|
13
|
+
# 检查并安装 Node.js
|
|
14
|
+
function Install-NodeJS {
|
|
15
|
+
Write-Host "正在检查 Node.js..." -ForegroundColor Cyan
|
|
16
|
+
|
|
17
|
+
if (Get-Command node -ErrorAction SilentlyContinue) {
|
|
18
|
+
Write-Host "Node.js 已安装: $(node --version)" -ForegroundColor Green
|
|
19
|
+
return $true
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
Write-Host "Node.js 未安装,正在安装..." -ForegroundColor Yellow
|
|
23
|
+
|
|
24
|
+
# 下载 Node.js LTS 安装包
|
|
25
|
+
$nodeUrl = "https://nodejs.org/dist/v20.11.0/node-v20.11.0-x64.msi"
|
|
26
|
+
$nodeInstaller = "$env:TEMP\node-installer.msi"
|
|
27
|
+
|
|
28
|
+
Write-Host "下载 Node.js..." -ForegroundColor Cyan
|
|
29
|
+
Invoke-WebRequest -Uri $nodeUrl -OutFile $nodeInstaller
|
|
30
|
+
|
|
31
|
+
Write-Host "安装 Node.js (需要管理员权限)..." -ForegroundColor Yellow
|
|
32
|
+
Start-Process msiexec.exe -ArgumentList "/i", $nodeInstaller, "/quiet", "/norestart" -Wait
|
|
33
|
+
|
|
34
|
+
# 刷新环境变量
|
|
35
|
+
$env:Path = [System.Environment]::GetEnvironmentVariable("Path", "Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path", "User")
|
|
36
|
+
|
|
37
|
+
# 验证安装
|
|
38
|
+
Start-Sleep -Seconds 2
|
|
39
|
+
if (Get-Command node -ErrorAction SilentlyContinue) {
|
|
40
|
+
Write-Host "Node.js 安装成功: $(node --version)" -ForegroundColor Green
|
|
41
|
+
Remove-Item $nodeInstaller -Force -ErrorAction SilentlyContinue
|
|
42
|
+
return $true
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
Write-Host "Node.js 安装失败,请手动安装" -ForegroundColor Red
|
|
46
|
+
Write-Host "下载地址: https://nodejs.org/" -ForegroundColor Yellow
|
|
47
|
+
return $false
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
# 检查并安装 Python
|
|
51
|
+
function Install-Python {
|
|
52
|
+
Write-Host "正在检查 Python..." -ForegroundColor Cyan
|
|
53
|
+
|
|
54
|
+
if (Get-Command python -ErrorAction SilentlyContinue) {
|
|
55
|
+
Write-Host "Python 已安装: $(python --version)" -ForegroundColor Green
|
|
56
|
+
return $true
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
Write-Host "Python 未安装,正在安装..." -ForegroundColor Yellow
|
|
60
|
+
|
|
61
|
+
# 下载 Python 安装包
|
|
62
|
+
$pythonUrl = "https://www.python.org/ftp/python/3.12.1/python-3.12.1-amd64.exe"
|
|
63
|
+
$pythonInstaller = "$env:TEMP\python-installer.exe"
|
|
64
|
+
|
|
65
|
+
Write-Host "下载 Python..." -ForegroundColor Cyan
|
|
66
|
+
Invoke-WebRequest -Uri $pythonUrl -OutFile $pythonInstaller
|
|
67
|
+
|
|
68
|
+
Write-Host "安装 Python (需要管理员权限)..." -ForegroundColor Yellow
|
|
69
|
+
# 使用静默安装参数,添加 PATH 和 pip
|
|
70
|
+
Start-Process $pythonInstaller -ArgumentList "/quiet", "InstallAllUsers=1", "PrependPath=1", "Include_pip=1" -Wait
|
|
71
|
+
|
|
72
|
+
# 刷新环境变量
|
|
73
|
+
$env:Path = [System.Environment]::GetEnvironmentVariable("Path", "Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path", "User")
|
|
74
|
+
|
|
75
|
+
# 验证安装
|
|
76
|
+
Start-Sleep -Seconds 2
|
|
77
|
+
if (Get-Command python -ErrorAction SilentlyContinue) {
|
|
78
|
+
Write-Host "Python 安装成功: $(python --version)" -ForegroundColor Green
|
|
79
|
+
Remove-Item $pythonInstaller -Force -ErrorAction SilentlyContinue
|
|
80
|
+
return $true
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
Write-Host "Python 安装失败,请手动安装" -ForegroundColor Red
|
|
84
|
+
Write-Host "下载地址: https://www.python.org/downloads/" -ForegroundColor Yellow
|
|
85
|
+
return $false
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
# 创建 Python 虚拟环境
|
|
89
|
+
function Install-PythonVenv {
|
|
90
|
+
param([string]$venvDir)
|
|
91
|
+
|
|
92
|
+
Write-Host "正在检查 Python venv..." -ForegroundColor Cyan
|
|
93
|
+
|
|
94
|
+
if (-not (Get-Command python -ErrorAction SilentlyContinue)) {
|
|
95
|
+
Write-Host "Python 未安装,跳过 venv" -ForegroundColor Yellow
|
|
96
|
+
return $false
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (Test-Path $venvDir) {
|
|
100
|
+
Write-Host "虚拟环境已存在: $venvDir" -ForegroundColor Green
|
|
101
|
+
return $true
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
Write-Host "创建虚拟环境: $venvDir" -ForegroundColor Cyan
|
|
105
|
+
python -m venv $venvDir
|
|
106
|
+
|
|
107
|
+
if (Test-Path "$venvDir\Scripts\python.exe") {
|
|
108
|
+
Write-Host "虚拟环境创建成功" -ForegroundColor Green
|
|
109
|
+
|
|
110
|
+
# 激活并安装基础包
|
|
111
|
+
Write-Host "安装基础依赖..." -ForegroundColor Cyan
|
|
112
|
+
& "$venvDir\Scripts\pip.exe" install pip --upgrade
|
|
113
|
+
return $true
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
Write-Host "虚拟环境创建失败" -ForegroundColor Red
|
|
117
|
+
return $false
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
# 主安装流程
|
|
121
|
+
Write-Host "环境检查" -ForegroundColor Cyan
|
|
122
|
+
Write-Host "-----------" -ForegroundColor Cyan
|
|
123
|
+
Write-Host ""
|
|
124
|
+
|
|
125
|
+
# 检查并安装依赖
|
|
126
|
+
$nodeOk = Install-NodeJS
|
|
127
|
+
Write-Host ""
|
|
128
|
+
$pythonOk = Install-Python
|
|
129
|
+
Write-Host ""
|
|
130
|
+
|
|
131
|
+
if (-not $nodeOk) {
|
|
132
|
+
Write-Host "警告: Node.js 未正确安装,部分功能可能不可用" -ForegroundColor Yellow
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (-not $pythonOk) {
|
|
136
|
+
Write-Host "警告: Python 未正确安装,部分功能可能不可用" -ForegroundColor Yellow
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
# 创建 Python 虚拟环境
|
|
140
|
+
$venvDir = "$InstallDir\venv"
|
|
141
|
+
if ($pythonOk) {
|
|
142
|
+
Write-Host ""
|
|
143
|
+
$venvOk = Install-PythonVenv -venvDir $venvDir
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
# 创建安装目录
|
|
147
|
+
if (-not (Test-Path $InstallDir)) {
|
|
148
|
+
New-Item -ItemType Directory -Path $InstallDir -Force | Out-Null
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
Write-Host "安装到: $InstallDir" -ForegroundColor Cyan
|
|
152
|
+
Write-Host ""
|
|
153
|
+
|
|
154
|
+
Push-Location $InstallDir
|
|
155
|
+
|
|
156
|
+
if (Test-Path ".git") {
|
|
157
|
+
Write-Host "更新现有安装..." -ForegroundColor Cyan
|
|
158
|
+
git pull
|
|
159
|
+
} else {
|
|
160
|
+
Write-Host "下载 Foliko..." -ForegroundColor Cyan
|
|
161
|
+
git clone https://github.com/$Repo.git .
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
# 安装依赖
|
|
165
|
+
Write-Host "安装 npm 依赖..." -ForegroundColor Cyan
|
|
166
|
+
npm install
|
|
167
|
+
|
|
168
|
+
# 全局链接 CLI
|
|
169
|
+
Write-Host "安装 CLI..." -ForegroundColor Cyan
|
|
170
|
+
npm link
|
|
171
|
+
|
|
172
|
+
Pop-Location
|
|
173
|
+
|
|
174
|
+
Write-Host ""
|
|
175
|
+
Write-Host "安装完成!" -ForegroundColor Green
|
|
176
|
+
Write-Host ""
|
|
177
|
+
Write-Host "使用方法:" -ForegroundColor Yellow
|
|
178
|
+
Write-Host " foliko chat" -ForegroundColor White
|
|
179
|
+
Write-Host ""
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "foliko",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "简约的插件化 Agent 框架",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -31,6 +31,6 @@
|
|
|
31
31
|
"marked": "^11.2.0",
|
|
32
32
|
"marked-terminal": "6",
|
|
33
33
|
"node-cron": "^4.2.1",
|
|
34
|
-
"zod": "^
|
|
34
|
+
"zod": "^3.24.0"
|
|
35
35
|
}
|
|
36
36
|
}
|
|
@@ -342,6 +342,15 @@ async function bootstrapDefaults(framework, config = {}) {
|
|
|
342
342
|
console.log('[Bootstrap] Think Plugin already loaded, skipping')
|
|
343
343
|
}
|
|
344
344
|
|
|
345
|
+
// 12.5 Python 插件加载器
|
|
346
|
+
if (!framework.pluginManager.has('python-plugin-loader')) {
|
|
347
|
+
const { PythonPluginLoader } = require('./python-plugin-loader')
|
|
348
|
+
await framework.loadPlugin(new PythonPluginLoader({ agentDir: agentConfig.agentDir }))
|
|
349
|
+
console.log('[Bootstrap] Python Plugin Loader loaded')
|
|
350
|
+
} else {
|
|
351
|
+
console.log('[Bootstrap] Python Plugin Loader already loaded, skipping')
|
|
352
|
+
}
|
|
353
|
+
|
|
345
354
|
// 13. 加载自定义插件
|
|
346
355
|
await loadCustomPlugins(framework, agentConfig)
|
|
347
356
|
|
|
@@ -42,10 +42,10 @@ class PythonExecutorPlugin extends Plugin {
|
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
start(framework) {
|
|
45
|
-
// 注册 python 工具
|
|
45
|
+
// 注册 python-execute 工具
|
|
46
46
|
framework.registerTool({
|
|
47
|
-
name: 'python',
|
|
48
|
-
description: '执行 Python
|
|
47
|
+
name: 'python-execute',
|
|
48
|
+
description: '执行 Python 代码,禁止传入无用的emoji',
|
|
49
49
|
inputSchema: z.object({
|
|
50
50
|
code: z.string().describe('Python 代码'),
|
|
51
51
|
timeout: z.number().optional().describe('超时时间(毫秒),默认 120000')
|