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.
Files changed (168) hide show
  1. fr_cli-2.4.0/MANIFEST.in +5 -0
  2. {fr_cli-2.3.2/fr_cli.egg-info → fr_cli-2.4.0}/PKG-INFO +63 -9
  3. {fr_cli-2.3.2 → fr_cli-2.4.0}/README.md +58 -6
  4. {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/WEAPON.MD +1 -1
  5. {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/__init__.py +1 -1
  6. {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/addon/plugin.py +2 -1
  7. {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/agent/__init__.py +1 -3
  8. {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/agent/a2a.py +2 -1
  9. {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/agent/acp.py +19 -15
  10. fr_cli-2.4.0/fr_cli/agent/artifact_detector.py +106 -0
  11. {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/agent/builtins/__init__.py +1 -1
  12. {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/agent/builtins/db.py +49 -6
  13. {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/agent/builtins/local.py +18 -3
  14. {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/agent/builtins/powerful_agent_template.py +3 -3
  15. {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/agent/builtins/rag.py +9 -8
  16. {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/agent/builtins/rag_watcher_daemon.py +7 -6
  17. {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/agent/builtins/remote.py +3 -2
  18. fr_cli-2.4.0/fr_cli/agent/builtins/spider.py +951 -0
  19. {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/agent/coding_helper.py +1 -1
  20. {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/agent/context_files.py +2 -1
  21. {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/agent/gateway.py +2 -1
  22. {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/agent/generator.py +4 -4
  23. {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/agent/hermes.py +4 -3
  24. fr_cli-2.4.0/fr_cli/agent/hermes_daemon.py +266 -0
  25. {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/agent/manager.py +16 -5
  26. {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/agent/master.py +36 -65
  27. {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/agent/personality.py +2 -1
  28. {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/agent/plugin_system.py +1 -1
  29. {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/agent/remote.py +3 -2
  30. {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/agent/skills.py +2 -1
  31. {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/agent/workflow.py +1 -1
  32. fr_cli-2.4.0/fr_cli/agent/workflow_system/__init__.py +14 -0
  33. fr_cli-2.4.0/fr_cli/agent/workflow_system/engine.py +414 -0
  34. fr_cli-2.4.0/fr_cli/agent/workflow_system/executor.py +217 -0
  35. fr_cli-2.4.0/fr_cli/agent/workflow_system/manager.py +84 -0
  36. fr_cli-2.4.0/fr_cli/agent/workflow_system/models.py +195 -0
  37. fr_cli-2.4.0/fr_cli/agent/workflow_system/monitor.py +68 -0
  38. fr_cli-2.4.0/fr_cli/agent/workflow_system/tools.py +224 -0
  39. fr_cli-2.4.0/fr_cli/agent/workflow_system.py +23 -0
  40. fr_cli-2.4.0/fr_cli/assets/splash.jpeg +0 -0
  41. {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/command/executor.py +66 -7
  42. fr_cli-2.4.0/fr_cli/command/handlers/__init__.py +15 -0
  43. fr_cli-2.4.0/fr_cli/command/handlers/agent.py +73 -0
  44. fr_cli-2.4.0/fr_cli/command/handlers/app.py +31 -0
  45. fr_cli-2.4.0/fr_cli/command/handlers/config.py +167 -0
  46. fr_cli-2.4.0/fr_cli/command/handlers/cron.py +56 -0
  47. fr_cli-2.4.0/fr_cli/command/handlers/dataframe.py +34 -0
  48. fr_cli-2.4.0/fr_cli/command/handlers/disk.py +84 -0
  49. fr_cli-2.4.0/fr_cli/command/handlers/fs.py +95 -0
  50. fr_cli-2.4.0/fr_cli/command/handlers/mail.py +81 -0
  51. fr_cli-2.4.0/fr_cli/command/handlers/mcp.py +46 -0
  52. fr_cli-2.4.0/fr_cli/command/handlers/other.py +34 -0
  53. fr_cli-2.4.0/fr_cli/command/handlers/session.py +79 -0
  54. fr_cli-2.4.0/fr_cli/command/handlers/system.py +189 -0
  55. fr_cli-2.4.0/fr_cli/command/handlers/vision.py +38 -0
  56. fr_cli-2.4.0/fr_cli/command/handlers/web.py +34 -0
  57. fr_cli-2.4.0/fr_cli/command/registered/agent_data_mcp.py +287 -0
  58. fr_cli-2.4.0/fr_cli/command/registered/fs.py +84 -0
  59. fr_cli-2.4.0/fr_cli/command/registered/image.py +37 -0
  60. fr_cli-2.4.0/fr_cli/command/registered/main_routes.py +47 -0
  61. fr_cli-2.4.0/fr_cli/command/registered/services.py +236 -0
  62. fr_cli-2.4.0/fr_cli/command/registered/session_config.py +309 -0
  63. fr_cli-2.4.0/fr_cli/command/registered/web.py +33 -0
  64. fr_cli-2.4.0/fr_cli/command/registry.py +280 -0
  65. fr_cli-2.4.0/fr_cli/conf/__init__.py +34 -0
  66. {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/conf/config.py +70 -29
  67. fr_cli-2.4.0/fr_cli/conf/paths.py +185 -0
  68. fr_cli-2.4.0/fr_cli/core/cache.py +107 -0
  69. {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/core/chat.py +72 -84
  70. {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/core/core.py +63 -18
  71. fr_cli-2.4.0/fr_cli/core/errors.py +202 -0
  72. {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/core/intent.py +4 -4
  73. {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/core/llm.py +110 -19
  74. {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/core/model_factory.py +61 -6
  75. fr_cli-2.4.0/fr_cli/core/optimizations.py +195 -0
  76. fr_cli-2.4.0/fr_cli/core/preferences.py +103 -0
  77. fr_cli-2.4.0/fr_cli/core/project.py +177 -0
  78. {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/core/recommender.py +38 -2
  79. fr_cli-2.4.0/fr_cli/core/stream.py +151 -0
  80. {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/core/thinking.py +12 -6
  81. {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/gatekeeper/__init__.py +1 -1
  82. {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/gatekeeper/daemon.py +6 -5
  83. {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/gatekeeper/manager.py +5 -4
  84. fr_cli-2.4.0/fr_cli/lang/i18n.py +8 -0
  85. fr_cli-2.4.0/fr_cli/lang/translations/__init__.py +9 -0
  86. fr_cli-2.4.0/fr_cli/lang/translations/en.py +164 -0
  87. fr_cli-2.4.0/fr_cli/lang/translations/zh.py +167 -0
  88. fr_cli-2.4.0/fr_cli/main.py +122 -0
  89. {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/memory/context.py +2 -1
  90. {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/memory/history.py +6 -5
  91. {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/memory/session.py +3 -2
  92. fr_cli-2.4.0/fr_cli/repl/actions.py +103 -0
  93. fr_cli-2.4.0/fr_cli/repl/bootstrap.py +166 -0
  94. fr_cli-2.4.0/fr_cli/repl/commands/__init__.py +43 -0
  95. fr_cli-2.4.0/fr_cli/repl/commands/_common.py +258 -0
  96. fr_cli-2.4.0/fr_cli/repl/commands/agent.py +305 -0
  97. fr_cli-2.4.0/fr_cli/repl/commands/base.py +269 -0
  98. fr_cli-2.4.0/fr_cli/repl/commands/config.py +375 -0
  99. fr_cli-2.4.0/fr_cli/repl/commands/cron.py +114 -0
  100. fr_cli-2.4.0/fr_cli/repl/commands/dataframe.py +60 -0
  101. fr_cli-2.4.0/fr_cli/repl/commands/dev.py +82 -0
  102. fr_cli-2.4.0/fr_cli/repl/commands/fs.py +66 -0
  103. fr_cli-2.4.0/fr_cli/repl/commands/mcp.py +142 -0
  104. fr_cli-2.4.0/fr_cli/repl/commands/rag.py +141 -0
  105. fr_cli-2.4.0/fr_cli/repl/commands/remote_agent.py +174 -0
  106. fr_cli-2.4.0/fr_cli/repl/commands/session.py +124 -0
  107. fr_cli-2.4.0/fr_cli/repl/commands/system.py +175 -0
  108. fr_cli-2.4.0/fr_cli/repl/commands.py +43 -0
  109. fr_cli-2.4.0/fr_cli/repl/queue.py +128 -0
  110. fr_cli-2.4.0/fr_cli/repl/quick_actions.py +227 -0
  111. fr_cli-2.4.0/fr_cli/repl/router.py +361 -0
  112. fr_cli-2.4.0/fr_cli/repl/scenarios.py +347 -0
  113. {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/security/security.py +6 -2
  114. fr_cli-2.4.0/fr_cli/ui/banner.py +134 -0
  115. fr_cli-2.4.0/fr_cli/ui/buddha.py +166 -0
  116. fr_cli-2.4.0/fr_cli/ui/prompt.py +837 -0
  117. fr_cli-2.4.0/fr_cli/ui/splash.py +340 -0
  118. {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/ui/ui.py +4 -4
  119. fr_cli-2.4.0/fr_cli/ui/web_config.py +233 -0
  120. {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/weapon/cron.py +43 -13
  121. {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/weapon/dataframe.py +2 -2
  122. {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/weapon/disk.py +2 -2
  123. {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/weapon/fs.py +1 -1
  124. {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/weapon/launcher.py +1 -1
  125. {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/weapon/loader.py +5 -5
  126. {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/weapon/mail.py +1 -1
  127. fr_cli-2.4.0/fr_cli/weapon/mcp.py +383 -0
  128. {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/weapon/vision.py +1 -1
  129. {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/weapon/web.py +1 -1
  130. {fr_cli-2.3.2 → fr_cli-2.4.0/fr_cli.egg-info}/PKG-INFO +63 -9
  131. {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli.egg-info/SOURCES.txt +67 -0
  132. {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli.egg-info/requires.txt +2 -0
  133. {fr_cli-2.3.2 → fr_cli-2.4.0}/pyproject.toml +5 -3
  134. fr_cli-2.4.0/tests/test_model_config.py +1208 -0
  135. {fr_cli-2.3.2 → fr_cli-2.4.0}/tests/test_new_providers.py +7 -7
  136. fr_cli-2.3.2/MANIFEST.in +0 -2
  137. fr_cli-2.3.2/fr_cli/agent/builtins/spider.py +0 -247
  138. fr_cli-2.3.2/fr_cli/agent/workflow_system.py +0 -1058
  139. fr_cli-2.3.2/fr_cli/command/registry.py +0 -1034
  140. fr_cli-2.3.2/fr_cli/core/stream.py +0 -83
  141. fr_cli-2.3.2/fr_cli/lang/i18n.py +0 -843
  142. fr_cli-2.3.2/fr_cli/main.py +0 -332
  143. fr_cli-2.3.2/fr_cli/repl/commands.py +0 -1418
  144. fr_cli-2.3.2/fr_cli/weapon/mcp.py +0 -188
  145. fr_cli-2.3.2/tests/test_model_config.py +0 -391
  146. {fr_cli-2.3.2 → fr_cli-2.4.0}/LICENSE +0 -0
  147. {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/README.md +0 -0
  148. {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/agent/builtins/_utils.py +0 -0
  149. {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/agent/client.py +0 -0
  150. {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/agent/executor.py +0 -0
  151. {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/agent/image_and_parallel.py +0 -0
  152. {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/agent/master_prompt.py +0 -0
  153. {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/agent/server.py +0 -0
  154. {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/agent/shell_mode.py +0 -0
  155. {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/breakthrough/update.py +0 -0
  156. {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/command/__init__.py +0 -0
  157. {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/command/security.py +0 -0
  158. {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/conf/wizard.py +0 -0
  159. {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/core/sysmon.py +0 -0
  160. {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli/repl/__init__.py +0 -0
  161. {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli.egg-info/dependency_links.txt +0 -0
  162. {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli.egg-info/entry_points.txt +0 -0
  163. {fr_cli-2.3.2 → fr_cli-2.4.0}/fr_cli.egg-info/top_level.txt +0 -0
  164. {fr_cli-2.3.2 → fr_cli-2.4.0}/setup.cfg +0 -0
  165. {fr_cli-2.3.2 → fr_cli-2.4.0}/tests/test_a2a_and_providers.py +0 -0
  166. {fr_cli-2.3.2 → fr_cli-2.4.0}/tests/test_integration_real.py +0 -0
  167. {fr_cli-2.3.2 → fr_cli-2.4.0}/tests/test_master_prompt_fix.py +0 -0
  168. {fr_cli-2.3.2 → fr_cli-2.4.0}/tests/test_new_features.py +0 -0
@@ -0,0 +1,5 @@
1
+ include fr_cli/WEAPON.MD
2
+ include fr_cli/README.md
3
+ include fr_cli/assets/*.jpeg
4
+ include fr_cli/assets/*.png
5
+ include fr_cli/assets/*.jpg
@@ -1,11 +1,11 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fr-cli
3
- Version: 2.3.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/yourname/fr-cli
8
- Project-URL: Issues, https://github.com/yourname/fr-cli/issues
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-cache-test (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
- ## Hermes 核心功能
242
+ ## 🧠 Hermes 核心功能
241
243
 
242
244
  ### 📋 任务管理
243
245
  ```
@@ -302,7 +304,7 @@ fr acp
302
304
  }
303
305
  ```
304
306
 
305
- ## �📦 支持的模型提供商(25+)
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-cache-test | api.kimi.com/coding/v1 |
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
- - [NEW_PROVIDERS_GUIDE.md](NEW_PROVIDERS_GUIDE.md) - 新增模型使用指南
368
- - [A2A_AND_PROVIDERS_GUIDE.md](A2A_AND_PROVIDERS_GUIDE.md) - A2A 协议文档
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-cache-test (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
- ## Hermes 核心功能
168
+ ## 🧠 Hermes 核心功能
169
169
 
170
170
  ### 📋 任务管理
171
171
  ```
@@ -230,7 +230,7 @@ fr acp
230
230
  }
231
231
  ```
232
232
 
233
- ## �📦 支持的模型提供商(25+)
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-cache-test | api.kimi.com/coding/v1 |
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
- - [NEW_PROVIDERS_GUIDE.md](NEW_PROVIDERS_GUIDE.md) - 新增模型使用指南
296
- - [A2A_AND_PROVIDERS_GUIDE.md](A2A_AND_PROVIDERS_GUIDE.md) - A2A 协议文档
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-cache-test) - 代码平台
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)
@@ -1,4 +1,4 @@
1
1
  """
2
2
  凡人打字机 - 基于智谱AI的终极全能终端工具
3
3
  """
4
- __version__ = "2.2.7"
4
+ __version__ = "2.4.0"
@@ -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 = Path.home() / ".zhipu_cli_plugins"
11
+ PLUGIN_DIR = PLUGIN_DIR # from fr_cli.conf.paths
11
12
 
12
13
  def init_plugins():
13
14
  """扫描并初始化本地插件字典 {名字: 绝对路径}"""
@@ -2,8 +2,6 @@
2
2
  Agent 分身系统
3
3
  支持创建、管理、执行由 AI 自动生成的独立 Agent
4
4
  """
5
- from pathlib import Path
6
-
7
- AGENTS_DIR = Path.home() / ".fr_cli_agents"
5
+ from fr_cli.conf.paths import AGENTS_DIR
8
6
 
9
7
  __all__ = ["AGENTS_DIR"]
@@ -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 = Path.home() / ".fr_cli_agent_registry.json"
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
- from fastapi.websockets import WebSocket
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.core.core import ask
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
- result, _ = await asyncio.to_thread(ask, message)
101
- return result
102
- except:
103
- return "Error processing request"
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
@@ -1,4 +1,4 @@
1
1
  """
2
- 内置 Agent 分身 —— 本命神通
2
+ 内置 Agent 分身 —— 本命工具/功能
3
3
  @local / @remote / @spider 等特殊前缀的 REPL 拦截器
4
4
  """
@@ -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 = Path.home() / ".fr_cli_databases.json"
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(f"SELECT column_name, data_type FROM information_schema.columns WHERE table_name = '{table}'")
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
- cursor.execute(f"SELECT column_name, data_type FROM user_tab_columns WHERE table_name = '{table}' ORDER BY column_id")
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
- sql = sql.strip().rstrip(";")
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 = subprocess.run(cmd_text, shell=True, capture_output=True, text=True, timeout=30)
89
+ res = _run_cmd_safely(cmd_text, timeout=30, allow_shell=True)
89
90
  else:
90
- res = subprocess.run(cmd_text, shell=True, capture_output=True, text=True, timeout=30)
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. 将此文件复制到 ~/.fr_cli_agents/<your_agent>/agent.py
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"~/.fr_cli_agents/{agent_name}/memory.md"
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将此模板复制到 ~/.fr_cli_agents/<your_agent>/agent.py 即可使用")
916
+ print("\n将此模板复制到 ~/.fr_cli/agents/<your_agent>/agent.py 即可使用")