fr-cli 2.3.2__tar.gz → 2.4.0__tar.gz
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.
- fr_cli-2.4.0/MANIFEST.in +5 -0
- {fr_cli-2.3.2/fr_cli.egg-info → fr_cli-2.4.0}/PKG-INFO +63 -9
- {fr_cli-2.3.2 → fr_cli-2.4.0}/README.md +58 -6
- {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/WEAPON.MD +1 -1
- {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/__init__.py +1 -1
- {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/addon/plugin.py +2 -1
- {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/agent/__init__.py +1 -3
- {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/agent/a2a.py +2 -1
- {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/agent/acp.py +19 -15
- fr_cli-2.4.0/fr_cli/agent/artifact_detector.py +106 -0
- {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/agent/builtins/__init__.py +1 -1
- {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/agent/builtins/db.py +49 -6
- {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/agent/builtins/local.py +18 -3
- {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/agent/builtins/powerful_agent_template.py +3 -3
- {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/agent/builtins/rag.py +9 -8
- {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/agent/builtins/rag_watcher_daemon.py +7 -6
- {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/agent/builtins/remote.py +3 -2
- fr_cli-2.4.0/fr_cli/agent/builtins/spider.py +951 -0
- {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/agent/coding_helper.py +1 -1
- {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/agent/context_files.py +2 -1
- {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/agent/gateway.py +2 -1
- {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/agent/generator.py +4 -4
- {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/agent/hermes.py +4 -3
- fr_cli-2.4.0/fr_cli/agent/hermes_daemon.py +266 -0
- {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/agent/manager.py +16 -5
- {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/agent/master.py +36 -65
- {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/agent/personality.py +2 -1
- {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/agent/plugin_system.py +1 -1
- {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/agent/remote.py +3 -2
- {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/agent/skills.py +2 -1
- {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/agent/workflow.py +1 -1
- fr_cli-2.4.0/fr_cli/agent/workflow_system/__init__.py +14 -0
- fr_cli-2.4.0/fr_cli/agent/workflow_system/engine.py +414 -0
- fr_cli-2.4.0/fr_cli/agent/workflow_system/executor.py +217 -0
- fr_cli-2.4.0/fr_cli/agent/workflow_system/manager.py +84 -0
- fr_cli-2.4.0/fr_cli/agent/workflow_system/models.py +195 -0
- fr_cli-2.4.0/fr_cli/agent/workflow_system/monitor.py +68 -0
- fr_cli-2.4.0/fr_cli/agent/workflow_system/tools.py +224 -0
- fr_cli-2.4.0/fr_cli/agent/workflow_system.py +23 -0
- fr_cli-2.4.0/fr_cli/assets/splash.jpeg +0 -0
- {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/command/executor.py +66 -7
- fr_cli-2.4.0/fr_cli/command/handlers/__init__.py +15 -0
- fr_cli-2.4.0/fr_cli/command/handlers/agent.py +73 -0
- fr_cli-2.4.0/fr_cli/command/handlers/app.py +31 -0
- fr_cli-2.4.0/fr_cli/command/handlers/config.py +167 -0
- fr_cli-2.4.0/fr_cli/command/handlers/cron.py +56 -0
- fr_cli-2.4.0/fr_cli/command/handlers/dataframe.py +34 -0
- fr_cli-2.4.0/fr_cli/command/handlers/disk.py +84 -0
- fr_cli-2.4.0/fr_cli/command/handlers/fs.py +95 -0
- fr_cli-2.4.0/fr_cli/command/handlers/mail.py +81 -0
- fr_cli-2.4.0/fr_cli/command/handlers/mcp.py +46 -0
- fr_cli-2.4.0/fr_cli/command/handlers/other.py +34 -0
- fr_cli-2.4.0/fr_cli/command/handlers/session.py +79 -0
- fr_cli-2.4.0/fr_cli/command/handlers/system.py +189 -0
- fr_cli-2.4.0/fr_cli/command/handlers/vision.py +38 -0
- fr_cli-2.4.0/fr_cli/command/handlers/web.py +34 -0
- fr_cli-2.4.0/fr_cli/command/registered/agent_data_mcp.py +287 -0
- fr_cli-2.4.0/fr_cli/command/registered/fs.py +84 -0
- fr_cli-2.4.0/fr_cli/command/registered/image.py +37 -0
- fr_cli-2.4.0/fr_cli/command/registered/main_routes.py +47 -0
- fr_cli-2.4.0/fr_cli/command/registered/services.py +236 -0
- fr_cli-2.4.0/fr_cli/command/registered/session_config.py +309 -0
- fr_cli-2.4.0/fr_cli/command/registered/web.py +33 -0
- fr_cli-2.4.0/fr_cli/command/registry.py +280 -0
- fr_cli-2.4.0/fr_cli/conf/__init__.py +34 -0
- {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/conf/config.py +70 -29
- fr_cli-2.4.0/fr_cli/conf/paths.py +185 -0
- fr_cli-2.4.0/fr_cli/core/cache.py +107 -0
- {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/core/chat.py +72 -84
- {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/core/core.py +63 -18
- fr_cli-2.4.0/fr_cli/core/errors.py +202 -0
- {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/core/intent.py +4 -4
- {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/core/llm.py +110 -19
- {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/core/model_factory.py +61 -6
- fr_cli-2.4.0/fr_cli/core/optimizations.py +195 -0
- fr_cli-2.4.0/fr_cli/core/preferences.py +103 -0
- fr_cli-2.4.0/fr_cli/core/project.py +177 -0
- {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/core/recommender.py +38 -2
- fr_cli-2.4.0/fr_cli/core/stream.py +151 -0
- {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/core/thinking.py +12 -6
- {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/gatekeeper/__init__.py +1 -1
- {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/gatekeeper/daemon.py +6 -5
- {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/gatekeeper/manager.py +5 -4
- fr_cli-2.4.0/fr_cli/lang/i18n.py +8 -0
- fr_cli-2.4.0/fr_cli/lang/translations/__init__.py +9 -0
- fr_cli-2.4.0/fr_cli/lang/translations/en.py +164 -0
- fr_cli-2.4.0/fr_cli/lang/translations/zh.py +167 -0
- fr_cli-2.4.0/fr_cli/main.py +122 -0
- {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/memory/context.py +2 -1
- {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/memory/history.py +6 -5
- {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/memory/session.py +3 -2
- fr_cli-2.4.0/fr_cli/repl/actions.py +103 -0
- fr_cli-2.4.0/fr_cli/repl/bootstrap.py +166 -0
- fr_cli-2.4.0/fr_cli/repl/commands/__init__.py +43 -0
- fr_cli-2.4.0/fr_cli/repl/commands/_common.py +258 -0
- fr_cli-2.4.0/fr_cli/repl/commands/agent.py +305 -0
- fr_cli-2.4.0/fr_cli/repl/commands/base.py +269 -0
- fr_cli-2.4.0/fr_cli/repl/commands/config.py +375 -0
- fr_cli-2.4.0/fr_cli/repl/commands/cron.py +114 -0
- fr_cli-2.4.0/fr_cli/repl/commands/dataframe.py +60 -0
- fr_cli-2.4.0/fr_cli/repl/commands/dev.py +82 -0
- fr_cli-2.4.0/fr_cli/repl/commands/fs.py +66 -0
- fr_cli-2.4.0/fr_cli/repl/commands/mcp.py +142 -0
- fr_cli-2.4.0/fr_cli/repl/commands/rag.py +141 -0
- fr_cli-2.4.0/fr_cli/repl/commands/remote_agent.py +174 -0
- fr_cli-2.4.0/fr_cli/repl/commands/session.py +124 -0
- fr_cli-2.4.0/fr_cli/repl/commands/system.py +175 -0
- fr_cli-2.4.0/fr_cli/repl/commands.py +43 -0
- fr_cli-2.4.0/fr_cli/repl/queue.py +128 -0
- fr_cli-2.4.0/fr_cli/repl/quick_actions.py +227 -0
- fr_cli-2.4.0/fr_cli/repl/router.py +361 -0
- fr_cli-2.4.0/fr_cli/repl/scenarios.py +347 -0
- {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/security/security.py +6 -2
- fr_cli-2.4.0/fr_cli/ui/banner.py +134 -0
- fr_cli-2.4.0/fr_cli/ui/buddha.py +166 -0
- fr_cli-2.4.0/fr_cli/ui/prompt.py +837 -0
- fr_cli-2.4.0/fr_cli/ui/splash.py +340 -0
- {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/ui/ui.py +4 -4
- fr_cli-2.4.0/fr_cli/ui/web_config.py +233 -0
- {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/weapon/cron.py +43 -13
- {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/weapon/dataframe.py +2 -2
- {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/weapon/disk.py +2 -2
- {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/weapon/fs.py +1 -1
- {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/weapon/launcher.py +1 -1
- {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/weapon/loader.py +5 -5
- {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/weapon/mail.py +1 -1
- fr_cli-2.4.0/fr_cli/weapon/mcp.py +383 -0
- {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/weapon/vision.py +1 -1
- {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/weapon/web.py +1 -1
- {fr_cli-2.3.2 → fr_cli-2.4.0/fr_cli.egg-info}/PKG-INFO +63 -9
- {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli.egg-info/SOURCES.txt +67 -0
- {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli.egg-info/requires.txt +2 -0
- {fr_cli-2.3.2 → fr_cli-2.4.0}/pyproject.toml +5 -3
- fr_cli-2.4.0/tests/test_model_config.py +1208 -0
- {fr_cli-2.3.2 → fr_cli-2.4.0}/tests/test_new_providers.py +7 -7
- fr_cli-2.3.2/MANIFEST.in +0 -2
- fr_cli-2.3.2/fr_cli/agent/builtins/spider.py +0 -247
- fr_cli-2.3.2/fr_cli/agent/workflow_system.py +0 -1058
- fr_cli-2.3.2/fr_cli/command/registry.py +0 -1034
- fr_cli-2.3.2/fr_cli/core/stream.py +0 -83
- fr_cli-2.3.2/fr_cli/lang/i18n.py +0 -843
- fr_cli-2.3.2/fr_cli/main.py +0 -332
- fr_cli-2.3.2/fr_cli/repl/commands.py +0 -1418
- fr_cli-2.3.2/fr_cli/weapon/mcp.py +0 -188
- fr_cli-2.3.2/tests/test_model_config.py +0 -391
- {fr_cli-2.3.2 → fr_cli-2.4.0}/LICENSE +0 -0
- {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/README.md +0 -0
- {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/agent/builtins/_utils.py +0 -0
- {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/agent/client.py +0 -0
- {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/agent/executor.py +0 -0
- {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/agent/image_and_parallel.py +0 -0
- {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/agent/master_prompt.py +0 -0
- {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/agent/server.py +0 -0
- {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/agent/shell_mode.py +0 -0
- {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/breakthrough/update.py +0 -0
- {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/command/__init__.py +0 -0
- {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/command/security.py +0 -0
- {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/conf/wizard.py +0 -0
- {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/core/sysmon.py +0 -0
- {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/repl/__init__.py +0 -0
- {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli.egg-info/dependency_links.txt +0 -0
- {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli.egg-info/entry_points.txt +0 -0
- {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli.egg-info/top_level.txt +0 -0
- {fr_cli-2.3.2 → fr_cli-2.4.0}/setup.cfg +0 -0
- {fr_cli-2.3.2 → fr_cli-2.4.0}/tests/test_a2a_and_providers.py +0 -0
- {fr_cli-2.3.2 → fr_cli-2.4.0}/tests/test_integration_real.py +0 -0
- {fr_cli-2.3.2 → fr_cli-2.4.0}/tests/test_master_prompt_fix.py +0 -0
- {fr_cli-2.3.2 → fr_cli-2.4.0}/tests/test_new_features.py +0 -0
fr_cli-2.4.0/MANIFEST.in
ADDED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fr-cli
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.4.0
|
|
4
4
|
Summary: 凡人打字机 - 支持多模型(Zhipu/DeepSeek/Kimi/Qwen/StepFun/MiniMax/Spark/Doubao/MiMo)的终极全能终端工具
|
|
5
5
|
Author: FANREN CLI Author
|
|
6
6
|
License-Expression: MIT
|
|
7
|
-
Project-URL: Homepage, https://github.com/
|
|
8
|
-
Project-URL: Issues, https://github.com/
|
|
7
|
+
Project-URL: Homepage, https://github.com/leungyukit/fr-cli
|
|
8
|
+
Project-URL: Issues, https://github.com/leungyukit/fr-cli/issues
|
|
9
9
|
Keywords: zhipuai,deepseek,kimi,qwen,cli,ai,terminal,chatbot
|
|
10
10
|
Classifier: Development Status :: 4 - Beta
|
|
11
11
|
Classifier: Environment :: Console
|
|
@@ -28,6 +28,8 @@ Requires-Dist: openai>=1.0.0
|
|
|
28
28
|
Requires-Dist: requests>=2.28.0
|
|
29
29
|
Requires-Dist: mcp>=1.6.0
|
|
30
30
|
Requires-Dist: pyyaml>=6.0.0
|
|
31
|
+
Requires-Dist: prompt_toolkit>=3.0.0
|
|
32
|
+
Requires-Dist: Pillow>=10.0.0
|
|
31
33
|
Provides-Extra: data
|
|
32
34
|
Requires-Dist: pandas>=1.5.0; extra == "data"
|
|
33
35
|
Requires-Dist: openpyxl>=3.0.0; extra == "data"
|
|
@@ -85,7 +87,7 @@ Dynamic: license-file
|
|
|
85
87
|
- **DeepSeek**: DeepSeek-Chat 等
|
|
86
88
|
- **Kimi (Moonshot)**: moonshot-v1-8k 等
|
|
87
89
|
- **Kimi K2**: 代码优化版 kimi-k2-0905-preview
|
|
88
|
-
- **Kimi Code**: 代码平台 kimi-
|
|
90
|
+
- **Kimi Code**: 代码平台 kimi-for-coding (Kimi 会员)
|
|
89
91
|
- **通义千问 (Qwen)**: qwen-turbo 等
|
|
90
92
|
- **阶跃星辰 (StepFun)**: step-1-8k, step-2-16k, step-3-auto 等
|
|
91
93
|
- **Step-Audio**: 实时语音交互
|
|
@@ -237,7 +239,7 @@ python3 main.py
|
|
|
237
239
|
/security 查看安全设置
|
|
238
240
|
```
|
|
239
241
|
|
|
240
|
-
##
|
|
242
|
+
## 🧠 Hermes 核心功能
|
|
241
243
|
|
|
242
244
|
### 📋 任务管理
|
|
243
245
|
```
|
|
@@ -302,7 +304,7 @@ fr acp
|
|
|
302
304
|
}
|
|
303
305
|
```
|
|
304
306
|
|
|
305
|
-
##
|
|
307
|
+
## 🤖 支持的模型提供商(25+)
|
|
306
308
|
|
|
307
309
|
| 道统 | 默认模型 | API 地址 |
|
|
308
310
|
|------|---------|----------|
|
|
@@ -312,7 +314,7 @@ fr acp
|
|
|
312
314
|
| deepseek | deepseek-chat | api.deepseek.com |
|
|
313
315
|
| kimi | moonshot-v1-8k | api.moonshot.cn |
|
|
314
316
|
| kimi-k2 | kimi-k2-0905-preview | api.moonshot.cn |
|
|
315
|
-
| kimi-code | kimi-
|
|
317
|
+
| kimi-code | kimi-for-coding | api.kimi.com/coding/v1 |
|
|
316
318
|
| qwen | qwen-turbo | dashscope.aliyuncs.com |
|
|
317
319
|
| stepfun | step-1-8k | api.stepfun.com |
|
|
318
320
|
| step-1 | step-1-8k | api.stepfun.com |
|
|
@@ -364,8 +366,60 @@ fr_cli/
|
|
|
364
366
|
|
|
365
367
|
## 📚 文档
|
|
366
368
|
|
|
367
|
-
- [
|
|
368
|
-
|
|
369
|
+
- [AGENTS.md](AGENTS.md) - 面向 AI 编码助手的项目架构与开发指南
|
|
370
|
+
|
|
371
|
+
## 📂 配置目录
|
|
372
|
+
|
|
373
|
+
| 路径 | 说明 |
|
|
374
|
+
|------|------|
|
|
375
|
+
| `~/.zhipu_cli_config.json` | 主配置文件 |
|
|
376
|
+
| `~/.zhipu_cli_history/` | 会话历史记录 |
|
|
377
|
+
| `~/.zhipu_cli_plugins/` | 用户插件目录 |
|
|
378
|
+
| `~/.zhipu_cli_context.json` | 上下文记忆 |
|
|
379
|
+
| `~/.fr_cli_agents/` | Agent 分身目录 |
|
|
380
|
+
| `~/.fr_cli_sessions/` | 按日期自动存档的会话 |
|
|
381
|
+
| `~/.fr_cli_master/` | MasterAgent 记忆与进化记录 |
|
|
382
|
+
| `~/.fr_cli_remotes.json` | 远程主机配置 |
|
|
383
|
+
| `~/.fr_cli_databases.json` | 数据库连接配置 |
|
|
384
|
+
| `~/.fr_cli_rag_db/` | RAG 向量库(ChromaDB)|
|
|
385
|
+
|
|
386
|
+
## ❓ 常见问题
|
|
387
|
+
|
|
388
|
+
**Q: 如何切换思维模式?**
|
|
389
|
+
```bash
|
|
390
|
+
/mode direct # 直接回复
|
|
391
|
+
/mode cot # 思维链
|
|
392
|
+
/mode tot # 思维树
|
|
393
|
+
/mode react # ReAct
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
**Q: 如何保存会话?**
|
|
397
|
+
```bash
|
|
398
|
+
/save my-session
|
|
399
|
+
/load
|
|
400
|
+
/export
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
**Q: 如何查看历史记录?**
|
|
404
|
+
```bash
|
|
405
|
+
/history
|
|
406
|
+
/session_list
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
**Q: 邮件发送失败?**
|
|
410
|
+
- QQ/163 邮箱需使用「授权码」而非登录密码
|
|
411
|
+
- 授权码在邮箱设置 → 账户 → 开启 IMAP/SMTP 服务后生成
|
|
412
|
+
|
|
413
|
+
**Q: 搜索功能无法使用?**
|
|
414
|
+
```bash
|
|
415
|
+
pip install requests
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
**Q: 云盘功能无法使用?**
|
|
419
|
+
```bash
|
|
420
|
+
pip install aligo # 阿里云盘
|
|
421
|
+
```
|
|
422
|
+
首次使用需运行 `/disk_setup` 完成扫码登录。
|
|
369
423
|
|
|
370
424
|
## 📄 License
|
|
371
425
|
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
- **DeepSeek**: DeepSeek-Chat 等
|
|
14
14
|
- **Kimi (Moonshot)**: moonshot-v1-8k 等
|
|
15
15
|
- **Kimi K2**: 代码优化版 kimi-k2-0905-preview
|
|
16
|
-
- **Kimi Code**: 代码平台 kimi-
|
|
16
|
+
- **Kimi Code**: 代码平台 kimi-for-coding (Kimi 会员)
|
|
17
17
|
- **通义千问 (Qwen)**: qwen-turbo 等
|
|
18
18
|
- **阶跃星辰 (StepFun)**: step-1-8k, step-2-16k, step-3-auto 等
|
|
19
19
|
- **Step-Audio**: 实时语音交互
|
|
@@ -165,7 +165,7 @@ python3 main.py
|
|
|
165
165
|
/security 查看安全设置
|
|
166
166
|
```
|
|
167
167
|
|
|
168
|
-
##
|
|
168
|
+
## 🧠 Hermes 核心功能
|
|
169
169
|
|
|
170
170
|
### 📋 任务管理
|
|
171
171
|
```
|
|
@@ -230,7 +230,7 @@ fr acp
|
|
|
230
230
|
}
|
|
231
231
|
```
|
|
232
232
|
|
|
233
|
-
##
|
|
233
|
+
## 🤖 支持的模型提供商(25+)
|
|
234
234
|
|
|
235
235
|
| 道统 | 默认模型 | API 地址 |
|
|
236
236
|
|------|---------|----------|
|
|
@@ -240,7 +240,7 @@ fr acp
|
|
|
240
240
|
| deepseek | deepseek-chat | api.deepseek.com |
|
|
241
241
|
| kimi | moonshot-v1-8k | api.moonshot.cn |
|
|
242
242
|
| kimi-k2 | kimi-k2-0905-preview | api.moonshot.cn |
|
|
243
|
-
| kimi-code | kimi-
|
|
243
|
+
| kimi-code | kimi-for-coding | api.kimi.com/coding/v1 |
|
|
244
244
|
| qwen | qwen-turbo | dashscope.aliyuncs.com |
|
|
245
245
|
| stepfun | step-1-8k | api.stepfun.com |
|
|
246
246
|
| step-1 | step-1-8k | api.stepfun.com |
|
|
@@ -292,8 +292,60 @@ fr_cli/
|
|
|
292
292
|
|
|
293
293
|
## 📚 文档
|
|
294
294
|
|
|
295
|
-
- [
|
|
296
|
-
|
|
295
|
+
- [AGENTS.md](AGENTS.md) - 面向 AI 编码助手的项目架构与开发指南
|
|
296
|
+
|
|
297
|
+
## 📂 配置目录
|
|
298
|
+
|
|
299
|
+
| 路径 | 说明 |
|
|
300
|
+
|------|------|
|
|
301
|
+
| `~/.zhipu_cli_config.json` | 主配置文件 |
|
|
302
|
+
| `~/.zhipu_cli_history/` | 会话历史记录 |
|
|
303
|
+
| `~/.zhipu_cli_plugins/` | 用户插件目录 |
|
|
304
|
+
| `~/.zhipu_cli_context.json` | 上下文记忆 |
|
|
305
|
+
| `~/.fr_cli_agents/` | Agent 分身目录 |
|
|
306
|
+
| `~/.fr_cli_sessions/` | 按日期自动存档的会话 |
|
|
307
|
+
| `~/.fr_cli_master/` | MasterAgent 记忆与进化记录 |
|
|
308
|
+
| `~/.fr_cli_remotes.json` | 远程主机配置 |
|
|
309
|
+
| `~/.fr_cli_databases.json` | 数据库连接配置 |
|
|
310
|
+
| `~/.fr_cli_rag_db/` | RAG 向量库(ChromaDB)|
|
|
311
|
+
|
|
312
|
+
## ❓ 常见问题
|
|
313
|
+
|
|
314
|
+
**Q: 如何切换思维模式?**
|
|
315
|
+
```bash
|
|
316
|
+
/mode direct # 直接回复
|
|
317
|
+
/mode cot # 思维链
|
|
318
|
+
/mode tot # 思维树
|
|
319
|
+
/mode react # ReAct
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
**Q: 如何保存会话?**
|
|
323
|
+
```bash
|
|
324
|
+
/save my-session
|
|
325
|
+
/load
|
|
326
|
+
/export
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
**Q: 如何查看历史记录?**
|
|
330
|
+
```bash
|
|
331
|
+
/history
|
|
332
|
+
/session_list
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
**Q: 邮件发送失败?**
|
|
336
|
+
- QQ/163 邮箱需使用「授权码」而非登录密码
|
|
337
|
+
- 授权码在邮箱设置 → 账户 → 开启 IMAP/SMTP 服务后生成
|
|
338
|
+
|
|
339
|
+
**Q: 搜索功能无法使用?**
|
|
340
|
+
```bash
|
|
341
|
+
pip install requests
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
**Q: 云盘功能无法使用?**
|
|
345
|
+
```bash
|
|
346
|
+
pip install aligo # 阿里云盘
|
|
347
|
+
```
|
|
348
|
+
首次使用需运行 `/disk_setup` 完成扫码登录。
|
|
297
349
|
|
|
298
350
|
## 📄 License
|
|
299
351
|
|
|
@@ -243,7 +243,7 @@ Agent 能力类型:
|
|
|
243
243
|
- DeepSeek: deepseek (deepseek-chat)
|
|
244
244
|
- Kimi: kimi (moonshot-v1-8k)
|
|
245
245
|
- Kimi K2: kimi-k2 (kimi-k2-0905-preview) - 代码优化版
|
|
246
|
-
- Kimi Code: kimi-code (kimi-
|
|
246
|
+
- Kimi Code: kimi-code (kimi-for-coding) - 代码平台
|
|
247
247
|
- 通义千问: qwen (qwen-turbo)
|
|
248
248
|
- 阶跃星辰: stepfun (step-1-8k), step-1, step-2, step-3, step-audio
|
|
249
249
|
- MiniMax: minimax (MiniMax-Text-01)
|
|
@@ -3,11 +3,12 @@
|
|
|
3
3
|
负责本地技能的扫描、动态落盘与沙盒执行
|
|
4
4
|
"""
|
|
5
5
|
import re, subprocess, sys
|
|
6
|
+
from fr_cli.conf.paths import PLUGIN_DIR
|
|
6
7
|
from pathlib import Path
|
|
7
8
|
from fr_cli.lang.i18n import T
|
|
8
9
|
from fr_cli.ui.ui import RED, DIM, RESET
|
|
9
10
|
|
|
10
|
-
PLUGIN_DIR =
|
|
11
|
+
PLUGIN_DIR = PLUGIN_DIR # from fr_cli.conf.paths
|
|
11
12
|
|
|
12
13
|
def init_plugins():
|
|
13
14
|
"""扫描并初始化本地插件字典 {名字: 绝对路径}"""
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
from fr_cli.conf.paths import REGISTRY_FILE
|
|
1
2
|
"""
|
|
2
3
|
Agent2Agent Protocol (A2A) - Agent 互操作协议
|
|
3
4
|
|
|
@@ -116,7 +117,7 @@ class AgentRegistry:
|
|
|
116
117
|
return
|
|
117
118
|
self._agents: Dict[str, AgentInfo] = {}
|
|
118
119
|
self._tasks: Dict[str, TaskResult] = {}
|
|
119
|
-
self._registry_file =
|
|
120
|
+
self._registry_file = REGISTRY_FILE
|
|
120
121
|
self._load_registry()
|
|
121
122
|
self._initialized = True
|
|
122
123
|
|
|
@@ -43,12 +43,11 @@ class ACPServer:
|
|
|
43
43
|
"""启动 ACP 服务器"""
|
|
44
44
|
self.port = port
|
|
45
45
|
self.running = True
|
|
46
|
-
|
|
46
|
+
|
|
47
47
|
try:
|
|
48
48
|
import uvicorn
|
|
49
|
-
from fastapi import FastAPI
|
|
50
|
-
|
|
51
|
-
|
|
49
|
+
from fastapi import FastAPI, WebSocket
|
|
50
|
+
|
|
52
51
|
app = FastAPI()
|
|
53
52
|
|
|
54
53
|
@app.websocket("/acp")
|
|
@@ -58,10 +57,10 @@ class ACPServer:
|
|
|
58
57
|
while self.running:
|
|
59
58
|
data = await websocket.receive_text()
|
|
60
59
|
message = json.loads(data)
|
|
61
|
-
|
|
60
|
+
|
|
62
61
|
# 处理 ACP 消息
|
|
63
62
|
response = await self.handle_message(message)
|
|
64
|
-
|
|
63
|
+
|
|
65
64
|
if response:
|
|
66
65
|
await websocket.send_text(json.dumps(response))
|
|
67
66
|
except Exception as e:
|
|
@@ -79,28 +78,33 @@ class ACPServer:
|
|
|
79
78
|
async def handle_message(self, message: Dict) -> Optional[Dict]:
|
|
80
79
|
"""处理 ACP 消息"""
|
|
81
80
|
msg_type = message.get("type")
|
|
82
|
-
|
|
81
|
+
|
|
83
82
|
if msg_type == "request":
|
|
84
83
|
method = message.get("method")
|
|
85
84
|
params = message.get("params", {})
|
|
86
|
-
|
|
85
|
+
|
|
87
86
|
if method == "chat":
|
|
88
87
|
result = await self.chat(params)
|
|
89
88
|
return {"id": message.get("id"), "type": "response", "result": result}
|
|
90
89
|
elif method == "tools":
|
|
91
90
|
return {"id": message.get("id"), "type": "response", "result": self.get_tools()}
|
|
92
|
-
|
|
91
|
+
|
|
93
92
|
return None
|
|
94
93
|
|
|
95
94
|
async def chat(self, params: Dict) -> str:
|
|
96
|
-
"""
|
|
97
|
-
from fr_cli.
|
|
95
|
+
"""处理聊天请求(通过 fr_cli 的统一入口调用 LLM)"""
|
|
96
|
+
from fr_cli.main import handle_ai_chat
|
|
97
|
+
from fr_cli.core.core import AppState
|
|
98
|
+
from fr_cli.conf.config import load_config
|
|
98
99
|
message = params.get("message", "")
|
|
99
100
|
try:
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
101
|
+
cfg = load_config()
|
|
102
|
+
# 构造一个临时 AppState 用于 LLM 调用(ACP 场景下 executor 可能未初始化)
|
|
103
|
+
state = AppState(cfg=cfg)
|
|
104
|
+
result, _ = await asyncio.to_thread(handle_ai_chat, state, message)
|
|
105
|
+
return result if result else ""
|
|
106
|
+
except Exception as e:
|
|
107
|
+
return f"Error processing request: {e}"
|
|
104
108
|
|
|
105
109
|
def get_tools(self) -> list:
|
|
106
110
|
"""获取可用工具列表"""
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
"""
|
|
2
|
+
AI 回复产物检测器 —— 统一检测插件/Agent 代码结构
|
|
3
|
+
|
|
4
|
+
提取自 core/chat.py 与 agent/master.py 的公共逻辑,
|
|
5
|
+
消除重复代码,保证行为一致性。
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import sys
|
|
9
|
+
from fr_cli.ui.ui import RED, GREEN, YELLOW, DIM, RESET
|
|
10
|
+
from fr_cli.lang.i18n import T
|
|
11
|
+
from fr_cli.addon.plugin import extract_code, PLUGIN_DIR
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def detect_plugin_artifact(txt: str, lang: str, state) -> bool:
|
|
15
|
+
"""检测 AI 回复中的插件代码结构,提示用户保存。
|
|
16
|
+
|
|
17
|
+
:param txt: AI 回复文本
|
|
18
|
+
:param lang: 界面语言
|
|
19
|
+
:param state: AppState 实例(需有 security 和 plugins 属性)
|
|
20
|
+
:return: 是否检测到并处理了插件
|
|
21
|
+
"""
|
|
22
|
+
if not txt or "def run(args='')" not in txt or "```python" not in txt:
|
|
23
|
+
return False
|
|
24
|
+
|
|
25
|
+
code = extract_code(txt)
|
|
26
|
+
if not code or "def run" not in code or len(code) <= 50:
|
|
27
|
+
return False
|
|
28
|
+
|
|
29
|
+
try:
|
|
30
|
+
pname = input(f"{YELLOW}{T('artifact_detect', lang)}{RESET}").strip()
|
|
31
|
+
if not pname:
|
|
32
|
+
return False
|
|
33
|
+
|
|
34
|
+
safe_name = "".join(c for c in pname if c.isalnum() or c == '_')
|
|
35
|
+
if not safe_name:
|
|
36
|
+
print(f"{RED}名称无效,仅允许字母/数字/下划线{RESET}")
|
|
37
|
+
return False
|
|
38
|
+
|
|
39
|
+
if hasattr(state, 'security') and state.security:
|
|
40
|
+
if not state.security.check("sec_write", f"/{safe_name}"):
|
|
41
|
+
return False
|
|
42
|
+
else:
|
|
43
|
+
# 无安全模块时直接放行(测试环境)
|
|
44
|
+
pass
|
|
45
|
+
|
|
46
|
+
PLUGIN_DIR.mkdir(parents=True, exist_ok=True)
|
|
47
|
+
p_path = PLUGIN_DIR / f"{safe_name}.py"
|
|
48
|
+
p_path.write_text(code, encoding='utf-8')
|
|
49
|
+
if hasattr(state, 'plugins'):
|
|
50
|
+
state.plugins[safe_name] = str(p_path)
|
|
51
|
+
print(f"{GREEN}{T('ok_forged', lang, safe_name)}{RESET}")
|
|
52
|
+
return True
|
|
53
|
+
except EOFError:
|
|
54
|
+
return False
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def detect_agent_artifact(txt: str, lang: str, state) -> bool:
|
|
58
|
+
"""检测 AI 回复中的 Agent 分身代码结构,提示用户保存。
|
|
59
|
+
|
|
60
|
+
:param txt: AI 回复文本
|
|
61
|
+
:param lang: 界面语言
|
|
62
|
+
:param state: AppState 实例(仅用于安全校验,可选)
|
|
63
|
+
:return: 是否检测到并处理了 Agent
|
|
64
|
+
"""
|
|
65
|
+
if not txt or "def run(context," not in txt or "```python" not in txt:
|
|
66
|
+
return False
|
|
67
|
+
|
|
68
|
+
code = extract_code(txt)
|
|
69
|
+
if not code or "def run(context," not in code or len(code) <= 50:
|
|
70
|
+
return False
|
|
71
|
+
|
|
72
|
+
try:
|
|
73
|
+
aname = input(f"{YELLOW}⚡ 检测到 Agent 分身结构,赐名 (回车放弃): {RESET}").strip()
|
|
74
|
+
if not aname:
|
|
75
|
+
return False
|
|
76
|
+
|
|
77
|
+
safe_name = "".join(c for c in aname if c.isalnum() or c == '_')
|
|
78
|
+
if not safe_name:
|
|
79
|
+
print(f"{RED}名称无效,仅允许字母/数字/下划线{RESET}")
|
|
80
|
+
return False
|
|
81
|
+
|
|
82
|
+
from fr_cli.agent.manager import (
|
|
83
|
+
create_agent_dir, save_agent_code, save_persona, save_skills, agent_exists
|
|
84
|
+
)
|
|
85
|
+
if agent_exists(safe_name):
|
|
86
|
+
confirm = input(
|
|
87
|
+
f"{YELLOW}Agent [{safe_name}] 已存在,是否覆盖? [y/N]: {RESET}"
|
|
88
|
+
).strip().lower()
|
|
89
|
+
if confirm not in ("y", "yes"):
|
|
90
|
+
print(f"{DIM}已取消。{RESET}")
|
|
91
|
+
return False
|
|
92
|
+
d = create_agent_dir(safe_name)
|
|
93
|
+
save_agent_code(safe_name, code)
|
|
94
|
+
print(f"{GREEN}✅ Agent [{safe_name}] 已覆盖更新。{RESET}")
|
|
95
|
+
print(f"{DIM} 路径: {d}{RESET}")
|
|
96
|
+
else:
|
|
97
|
+
d = create_agent_dir(safe_name)
|
|
98
|
+
save_agent_code(safe_name, code)
|
|
99
|
+
save_persona(safe_name, f"#{safe_name}\n\n由 AI 对话创建的 Agent 分身。")
|
|
100
|
+
save_skills(safe_name, "## 技能\n\n- 执行自定义 Python 逻辑\n- 入口: run(context, **kwargs)")
|
|
101
|
+
print(f"{GREEN}✅ Agent [{safe_name}] 创建完成!{RESET}")
|
|
102
|
+
print(f"{DIM} 路径: {d}{RESET}")
|
|
103
|
+
print(f"{DIM} 运行: /agent_run {safe_name} [参数]{RESET}")
|
|
104
|
+
return True
|
|
105
|
+
except EOFError:
|
|
106
|
+
return False
|
|
@@ -3,8 +3,9 @@
|
|
|
3
3
|
支持 MySQL / PostgreSQL / SQL Server / Oracle 的 Schema 分析和 SQL 生成。
|
|
4
4
|
"""
|
|
5
5
|
from pathlib import Path
|
|
6
|
+
from fr_cli.conf.paths import DATABASE_FILE
|
|
6
7
|
|
|
7
|
-
DB_CFG_PATH =
|
|
8
|
+
DB_CFG_PATH = DATABASE_FILE # from fr_cli.conf.paths
|
|
8
9
|
|
|
9
10
|
DB_SYS_PROMPT = """你是一个数据库专家。请根据以下数据库 Schema 信息和用户需求,生成最合适的 SQL 语句。
|
|
10
11
|
|
|
@@ -104,8 +105,12 @@ def _get_schema_info(conn, db_type):
|
|
|
104
105
|
cursor.execute("SELECT table_name FROM information_schema.tables WHERE table_type = 'BASE TABLE'")
|
|
105
106
|
tables = [r[0] for r in cursor.fetchall()]
|
|
106
107
|
for table in tables:
|
|
108
|
+
# 防御:表名可能含特殊字符(罕见但合法),用参数化
|
|
107
109
|
info.append(f"\n表: {table}")
|
|
108
|
-
cursor.execute(
|
|
110
|
+
cursor.execute(
|
|
111
|
+
"SELECT column_name, data_type FROM information_schema.columns WHERE table_name = ?",
|
|
112
|
+
(table,),
|
|
113
|
+
)
|
|
109
114
|
for col in cursor.fetchall():
|
|
110
115
|
info.append(f" {col[0]} {col[1]}")
|
|
111
116
|
elif db_type == "oracle":
|
|
@@ -113,19 +118,57 @@ def _get_schema_info(conn, db_type):
|
|
|
113
118
|
tables = [r[0] for r in cursor.fetchall()]
|
|
114
119
|
for table in tables:
|
|
115
120
|
info.append(f"\n表: {table}")
|
|
116
|
-
|
|
121
|
+
# Oracle: 表名是 Oracle 标识符;用绑定变量同样安全
|
|
122
|
+
cursor.execute(
|
|
123
|
+
"SELECT column_name, data_type FROM user_tab_columns WHERE table_name = :t ORDER BY column_id",
|
|
124
|
+
{"t": table},
|
|
125
|
+
)
|
|
117
126
|
for col in cursor.fetchall():
|
|
118
127
|
info.append(f" {col[0]} {col[1]}")
|
|
119
128
|
|
|
120
129
|
return "\n".join(info)
|
|
121
130
|
|
|
122
131
|
|
|
132
|
+
def _has_multiple_statements(sql: str) -> bool:
|
|
133
|
+
"""检测 SQL 是否包含多条语句(启发式:去掉字符串字面量后是否还有 ;)
|
|
134
|
+
|
|
135
|
+
用于阻断 SELECT 1; DELETE FROM users 这类多语句注入。
|
|
136
|
+
末尾的 ; 不算(先 rstrip 掉)。
|
|
137
|
+
"""
|
|
138
|
+
# 先去掉末尾空白和分号
|
|
139
|
+
sql_stripped = sql.rstrip(" \t\r\n;")
|
|
140
|
+
|
|
141
|
+
cleaned = []
|
|
142
|
+
in_string = False
|
|
143
|
+
escape = False
|
|
144
|
+
quote_char = None
|
|
145
|
+
for ch in sql_stripped:
|
|
146
|
+
if escape:
|
|
147
|
+
escape = False
|
|
148
|
+
continue
|
|
149
|
+
if ch == "\\" and in_string:
|
|
150
|
+
escape = True
|
|
151
|
+
continue
|
|
152
|
+
if in_string:
|
|
153
|
+
if ch == quote_char:
|
|
154
|
+
in_string = False
|
|
155
|
+
continue
|
|
156
|
+
if ch in ("'", '"'):
|
|
157
|
+
in_string = True
|
|
158
|
+
quote_char = ch
|
|
159
|
+
continue
|
|
160
|
+
cleaned.append(ch)
|
|
161
|
+
return ";" in "".join(cleaned)
|
|
162
|
+
|
|
163
|
+
|
|
123
164
|
def _exec_sql(conn, sql, db_type):
|
|
124
165
|
"""执行 SQL 并返回结果"""
|
|
125
166
|
cursor = conn.cursor()
|
|
126
167
|
try:
|
|
127
|
-
|
|
128
|
-
|
|
168
|
+
sql = sql.strip()
|
|
169
|
+
if _has_multiple_statements(sql):
|
|
170
|
+
return None, "禁止执行多条 SQL 语句(检测到语句分隔符 ';')"
|
|
171
|
+
sql = sql.rstrip(";")
|
|
129
172
|
cursor.execute(sql)
|
|
130
173
|
|
|
131
174
|
# 如果是 SELECT,返回结果集
|
|
@@ -199,7 +242,7 @@ def handle_db(user_input, state):
|
|
|
199
242
|
]
|
|
200
243
|
|
|
201
244
|
print(f"{CYAN}🧙 正在生成 SQL...{RESET}")
|
|
202
|
-
sql_text, _, _ = stream_cnt(state.client, state.model_name, messages, state.lang, custom_prefix="", max_tokens=1024)
|
|
245
|
+
sql_text, _, _, _ = stream_cnt(state.client, state.model_name, messages, state.lang, custom_prefix="", max_tokens=1024)
|
|
203
246
|
sql_text = sql_text.strip()
|
|
204
247
|
|
|
205
248
|
from fr_cli.agent.builtins._utils import strip_code_blocks
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
import platform
|
|
6
6
|
import shutil
|
|
7
7
|
import subprocess
|
|
8
|
+
import shlex
|
|
8
9
|
|
|
9
10
|
LOCAL_SYS_PROMPT = """你是一个系统命令专家。请根据用户的操作系统类型和需求,生成最合适、最安全的系统命令。
|
|
10
11
|
|
|
@@ -56,7 +57,7 @@ def handle_local(user_input, state):
|
|
|
56
57
|
]
|
|
57
58
|
|
|
58
59
|
print(f"{CYAN}🧙 正在分析本地操作...{RESET}")
|
|
59
|
-
cmd_text, _, _ = stream_cnt(state.client, state.model_name, messages, state.lang, custom_prefix="", max_tokens=1024)
|
|
60
|
+
cmd_text, _, _, _ = stream_cnt(state.client, state.model_name, messages, state.lang, custom_prefix="", max_tokens=1024)
|
|
60
61
|
cmd_text = cmd_text.strip()
|
|
61
62
|
|
|
62
63
|
# 清理可能的代码块
|
|
@@ -85,9 +86,9 @@ def handle_local(user_input, state):
|
|
|
85
86
|
if ps_exe:
|
|
86
87
|
res = subprocess.run([ps_exe, "-Command", cmd_text], capture_output=True, text=True, timeout=30)
|
|
87
88
|
else:
|
|
88
|
-
res =
|
|
89
|
+
res = _run_cmd_safely(cmd_text, timeout=30, allow_shell=True)
|
|
89
90
|
else:
|
|
90
|
-
res =
|
|
91
|
+
res = _run_cmd_safely(cmd_text, timeout=30)
|
|
91
92
|
out = res.stdout + res.stderr
|
|
92
93
|
if out.strip():
|
|
93
94
|
print(f"\n{GREEN}{out.strip()[:3000]}{RESET}")
|
|
@@ -103,3 +104,17 @@ def handle_local(user_input, state):
|
|
|
103
104
|
print(f"{RED}⏱️ 命令执行超时(30秒){RESET}")
|
|
104
105
|
except Exception as e:
|
|
105
106
|
print(f"{RED}❌ 执行失败: {e}{RESET}")
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def _run_cmd_safely(cmd_text: str, timeout: int = 30, allow_shell: bool = False):
|
|
110
|
+
"""安全执行命令:POSIX 用 shlex.split + shell=False;仅在显式 allow_shell=True 时降级。"""
|
|
111
|
+
try:
|
|
112
|
+
cmd_list = shlex.split(cmd_text)
|
|
113
|
+
except ValueError:
|
|
114
|
+
# shlex 解析失败(包含未闭合引号等)—— 视情况降级
|
|
115
|
+
if allow_shell:
|
|
116
|
+
return subprocess.run(cmd_text, shell=True, capture_output=True, text=True, timeout=timeout)
|
|
117
|
+
return subprocess.CompletedProcess(args=cmd_text, returncode=1, stdout="", stderr="命令解析失败")
|
|
118
|
+
if not cmd_list:
|
|
119
|
+
return subprocess.CompletedProcess(args=cmd_text, returncode=1, stdout="", stderr="空命令")
|
|
120
|
+
return subprocess.run(cmd_list, shell=False, capture_output=True, text=True, timeout=timeout)
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
- 多轮对话和任务执行
|
|
12
12
|
|
|
13
13
|
使用方法:
|
|
14
|
-
1. 将此文件复制到 ~/.
|
|
14
|
+
1. 将此文件复制到 ~/.fr_cli/agents/<your_agent>/agent.py
|
|
15
15
|
2. 编辑 persona.md, memory.md, skills.md 配置 Agent 角色
|
|
16
16
|
3. 在 config.json 中设置专属模型配置
|
|
17
17
|
|
|
@@ -281,7 +281,7 @@ class MemorySystem:
|
|
|
281
281
|
|
|
282
282
|
def __init__(self, agent_name: str, memory_file: str = None):
|
|
283
283
|
self.agent_name = agent_name
|
|
284
|
-
self.memory_file = memory_file or f"~/.
|
|
284
|
+
self.memory_file = memory_file or f"~/.fr_cli/agents/{agent_name}/memory.md"
|
|
285
285
|
self.short_term: List[MemoryEntry] = []
|
|
286
286
|
self.long_term: List[MemoryEntry] = []
|
|
287
287
|
self.max_short_term = 50 # 短期记忆最大条数
|
|
@@ -913,4 +913,4 @@ if __name__ == "__main__":
|
|
|
913
913
|
print("可用工具: read_file, write_file, list_files, search_files,")
|
|
914
914
|
print(" web_search, fetch_url, execute_code, run_command,")
|
|
915
915
|
print(" db_query, call_agent, delegate_task")
|
|
916
|
-
print("\n将此模板复制到 ~/.
|
|
916
|
+
print("\n将此模板复制到 ~/.fr_cli/agents/<your_agent>/agent.py 即可使用")
|