fr-cli 2.3.1__tar.gz → 2.3.3__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 (92) hide show
  1. {fr_cli-2.3.1/fr_cli.egg-info → fr_cli-2.3.3}/PKG-INFO +1 -1
  2. fr_cli-2.3.3/fr_cli/agent/hermes_daemon.py +199 -0
  3. {fr_cli-2.3.1 → fr_cli-2.3.3}/fr_cli/core/core.py +1 -1
  4. {fr_cli-2.3.1 → fr_cli-2.3.3}/fr_cli/main.py +4 -0
  5. {fr_cli-2.3.1 → fr_cli-2.3.3}/fr_cli/repl/commands.py +70 -0
  6. {fr_cli-2.3.1 → fr_cli-2.3.3}/fr_cli/weapon/mcp.py +3 -1
  7. {fr_cli-2.3.1 → fr_cli-2.3.3/fr_cli.egg-info}/PKG-INFO +1 -1
  8. {fr_cli-2.3.1 → fr_cli-2.3.3}/fr_cli.egg-info/SOURCES.txt +1 -0
  9. {fr_cli-2.3.1 → fr_cli-2.3.3}/pyproject.toml +1 -1
  10. {fr_cli-2.3.1 → fr_cli-2.3.3}/LICENSE +0 -0
  11. {fr_cli-2.3.1 → fr_cli-2.3.3}/MANIFEST.in +0 -0
  12. {fr_cli-2.3.1 → fr_cli-2.3.3}/README.md +0 -0
  13. {fr_cli-2.3.1 → fr_cli-2.3.3}/fr_cli/README.md +0 -0
  14. {fr_cli-2.3.1 → fr_cli-2.3.3}/fr_cli/WEAPON.MD +0 -0
  15. {fr_cli-2.3.1 → fr_cli-2.3.3}/fr_cli/__init__.py +0 -0
  16. {fr_cli-2.3.1 → fr_cli-2.3.3}/fr_cli/addon/plugin.py +0 -0
  17. {fr_cli-2.3.1 → fr_cli-2.3.3}/fr_cli/agent/__init__.py +0 -0
  18. {fr_cli-2.3.1 → fr_cli-2.3.3}/fr_cli/agent/a2a.py +0 -0
  19. {fr_cli-2.3.1 → fr_cli-2.3.3}/fr_cli/agent/acp.py +0 -0
  20. {fr_cli-2.3.1 → fr_cli-2.3.3}/fr_cli/agent/builtins/__init__.py +0 -0
  21. {fr_cli-2.3.1 → fr_cli-2.3.3}/fr_cli/agent/builtins/_utils.py +0 -0
  22. {fr_cli-2.3.1 → fr_cli-2.3.3}/fr_cli/agent/builtins/db.py +0 -0
  23. {fr_cli-2.3.1 → fr_cli-2.3.3}/fr_cli/agent/builtins/local.py +0 -0
  24. {fr_cli-2.3.1 → fr_cli-2.3.3}/fr_cli/agent/builtins/powerful_agent_template.py +0 -0
  25. {fr_cli-2.3.1 → fr_cli-2.3.3}/fr_cli/agent/builtins/rag.py +0 -0
  26. {fr_cli-2.3.1 → fr_cli-2.3.3}/fr_cli/agent/builtins/rag_watcher_daemon.py +0 -0
  27. {fr_cli-2.3.1 → fr_cli-2.3.3}/fr_cli/agent/builtins/remote.py +0 -0
  28. {fr_cli-2.3.1 → fr_cli-2.3.3}/fr_cli/agent/builtins/spider.py +0 -0
  29. {fr_cli-2.3.1 → fr_cli-2.3.3}/fr_cli/agent/client.py +0 -0
  30. {fr_cli-2.3.1 → fr_cli-2.3.3}/fr_cli/agent/coding_helper.py +0 -0
  31. {fr_cli-2.3.1 → fr_cli-2.3.3}/fr_cli/agent/context_files.py +0 -0
  32. {fr_cli-2.3.1 → fr_cli-2.3.3}/fr_cli/agent/executor.py +0 -0
  33. {fr_cli-2.3.1 → fr_cli-2.3.3}/fr_cli/agent/gateway.py +0 -0
  34. {fr_cli-2.3.1 → fr_cli-2.3.3}/fr_cli/agent/generator.py +0 -0
  35. {fr_cli-2.3.1 → fr_cli-2.3.3}/fr_cli/agent/hermes.py +0 -0
  36. {fr_cli-2.3.1 → fr_cli-2.3.3}/fr_cli/agent/image_and_parallel.py +0 -0
  37. {fr_cli-2.3.1 → fr_cli-2.3.3}/fr_cli/agent/manager.py +0 -0
  38. {fr_cli-2.3.1 → fr_cli-2.3.3}/fr_cli/agent/master.py +0 -0
  39. {fr_cli-2.3.1 → fr_cli-2.3.3}/fr_cli/agent/master_prompt.py +0 -0
  40. {fr_cli-2.3.1 → fr_cli-2.3.3}/fr_cli/agent/personality.py +0 -0
  41. {fr_cli-2.3.1 → fr_cli-2.3.3}/fr_cli/agent/plugin_system.py +0 -0
  42. {fr_cli-2.3.1 → fr_cli-2.3.3}/fr_cli/agent/remote.py +0 -0
  43. {fr_cli-2.3.1 → fr_cli-2.3.3}/fr_cli/agent/server.py +0 -0
  44. {fr_cli-2.3.1 → fr_cli-2.3.3}/fr_cli/agent/shell_mode.py +0 -0
  45. {fr_cli-2.3.1 → fr_cli-2.3.3}/fr_cli/agent/skills.py +0 -0
  46. {fr_cli-2.3.1 → fr_cli-2.3.3}/fr_cli/agent/workflow.py +0 -0
  47. {fr_cli-2.3.1 → fr_cli-2.3.3}/fr_cli/agent/workflow_system.py +0 -0
  48. {fr_cli-2.3.1 → fr_cli-2.3.3}/fr_cli/breakthrough/update.py +0 -0
  49. {fr_cli-2.3.1 → fr_cli-2.3.3}/fr_cli/command/__init__.py +0 -0
  50. {fr_cli-2.3.1 → fr_cli-2.3.3}/fr_cli/command/executor.py +0 -0
  51. {fr_cli-2.3.1 → fr_cli-2.3.3}/fr_cli/command/registry.py +0 -0
  52. {fr_cli-2.3.1 → fr_cli-2.3.3}/fr_cli/command/security.py +0 -0
  53. {fr_cli-2.3.1 → fr_cli-2.3.3}/fr_cli/conf/config.py +0 -0
  54. {fr_cli-2.3.1 → fr_cli-2.3.3}/fr_cli/conf/wizard.py +0 -0
  55. {fr_cli-2.3.1 → fr_cli-2.3.3}/fr_cli/core/chat.py +0 -0
  56. {fr_cli-2.3.1 → fr_cli-2.3.3}/fr_cli/core/intent.py +0 -0
  57. {fr_cli-2.3.1 → fr_cli-2.3.3}/fr_cli/core/llm.py +0 -0
  58. {fr_cli-2.3.1 → fr_cli-2.3.3}/fr_cli/core/model_factory.py +0 -0
  59. {fr_cli-2.3.1 → fr_cli-2.3.3}/fr_cli/core/recommender.py +0 -0
  60. {fr_cli-2.3.1 → fr_cli-2.3.3}/fr_cli/core/stream.py +0 -0
  61. {fr_cli-2.3.1 → fr_cli-2.3.3}/fr_cli/core/sysmon.py +0 -0
  62. {fr_cli-2.3.1 → fr_cli-2.3.3}/fr_cli/core/thinking.py +0 -0
  63. {fr_cli-2.3.1 → fr_cli-2.3.3}/fr_cli/gatekeeper/__init__.py +0 -0
  64. {fr_cli-2.3.1 → fr_cli-2.3.3}/fr_cli/gatekeeper/daemon.py +0 -0
  65. {fr_cli-2.3.1 → fr_cli-2.3.3}/fr_cli/gatekeeper/manager.py +0 -0
  66. {fr_cli-2.3.1 → fr_cli-2.3.3}/fr_cli/lang/i18n.py +0 -0
  67. {fr_cli-2.3.1 → fr_cli-2.3.3}/fr_cli/memory/context.py +0 -0
  68. {fr_cli-2.3.1 → fr_cli-2.3.3}/fr_cli/memory/history.py +0 -0
  69. {fr_cli-2.3.1 → fr_cli-2.3.3}/fr_cli/memory/session.py +0 -0
  70. {fr_cli-2.3.1 → fr_cli-2.3.3}/fr_cli/repl/__init__.py +0 -0
  71. {fr_cli-2.3.1 → fr_cli-2.3.3}/fr_cli/security/security.py +0 -0
  72. {fr_cli-2.3.1 → fr_cli-2.3.3}/fr_cli/ui/ui.py +0 -0
  73. {fr_cli-2.3.1 → fr_cli-2.3.3}/fr_cli/weapon/cron.py +0 -0
  74. {fr_cli-2.3.1 → fr_cli-2.3.3}/fr_cli/weapon/dataframe.py +0 -0
  75. {fr_cli-2.3.1 → fr_cli-2.3.3}/fr_cli/weapon/disk.py +0 -0
  76. {fr_cli-2.3.1 → fr_cli-2.3.3}/fr_cli/weapon/fs.py +0 -0
  77. {fr_cli-2.3.1 → fr_cli-2.3.3}/fr_cli/weapon/launcher.py +0 -0
  78. {fr_cli-2.3.1 → fr_cli-2.3.3}/fr_cli/weapon/loader.py +0 -0
  79. {fr_cli-2.3.1 → fr_cli-2.3.3}/fr_cli/weapon/mail.py +0 -0
  80. {fr_cli-2.3.1 → fr_cli-2.3.3}/fr_cli/weapon/vision.py +0 -0
  81. {fr_cli-2.3.1 → fr_cli-2.3.3}/fr_cli/weapon/web.py +0 -0
  82. {fr_cli-2.3.1 → fr_cli-2.3.3}/fr_cli.egg-info/dependency_links.txt +0 -0
  83. {fr_cli-2.3.1 → fr_cli-2.3.3}/fr_cli.egg-info/entry_points.txt +0 -0
  84. {fr_cli-2.3.1 → fr_cli-2.3.3}/fr_cli.egg-info/requires.txt +0 -0
  85. {fr_cli-2.3.1 → fr_cli-2.3.3}/fr_cli.egg-info/top_level.txt +0 -0
  86. {fr_cli-2.3.1 → fr_cli-2.3.3}/setup.cfg +0 -0
  87. {fr_cli-2.3.1 → fr_cli-2.3.3}/tests/test_a2a_and_providers.py +0 -0
  88. {fr_cli-2.3.1 → fr_cli-2.3.3}/tests/test_integration_real.py +0 -0
  89. {fr_cli-2.3.1 → fr_cli-2.3.3}/tests/test_master_prompt_fix.py +0 -0
  90. {fr_cli-2.3.1 → fr_cli-2.3.3}/tests/test_model_config.py +0 -0
  91. {fr_cli-2.3.1 → fr_cli-2.3.3}/tests/test_new_features.py +0 -0
  92. {fr_cli-2.3.1 → fr_cli-2.3.3}/tests/test_new_providers.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fr-cli
3
- Version: 2.3.1
3
+ Version: 2.3.3
4
4
  Summary: 凡人打字机 - 支持多模型(Zhipu/DeepSeek/Kimi/Qwen/StepFun/MiniMax/Spark/Doubao/MiMo)的终极全能终端工具
5
5
  Author: FANREN CLI Author
6
6
  License-Expression: MIT
@@ -0,0 +1,199 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Hermes 守护进程 - 后台服务接收终端命令
4
+ 用法: /hermes start
5
+ """
6
+
7
+ import os
8
+ import sys
9
+ import json
10
+ import time
11
+ import subprocess
12
+ import threading
13
+ from http.server import HTTPServer, BaseHTTPRequestHandler
14
+
15
+ class HermesDaemon:
16
+ """Hermes 守护进程 - 后台服务"""
17
+
18
+ def __init__(self, port=8765, host="127.0.0.1"):
19
+ self.port = port
20
+ self.host = host
21
+ self.tasks = []
22
+ self.skills = []
23
+ self.goals = []
24
+ self.analytics = {"requests": 0, "tokens": 0, "cost": 0.0}
25
+ self.running = True
26
+
27
+ def start(self):
28
+ """启动守护进程"""
29
+ server = HTTPServer((self.host, self.port), HermesHandler)
30
+ server.daemon = self
31
+ print(f"🧚 Hermes 守护进程已启动: http://{self.host}:{self.port}")
32
+ print("📡 监听命令中...")
33
+
34
+ while self.running:
35
+ try:
36
+ server.handle_request()
37
+ except Exception:
38
+ pass
39
+
40
+
41
+ class HermesHandler(BaseHTTPRequestHandler):
42
+ """HTTP 请求处理器"""
43
+
44
+ def log_message(self, format, *args):
45
+ pass
46
+
47
+ def send_json(self, status, data):
48
+ self.send_response(status)
49
+ self.send_header("Content-Type", "application/json")
50
+ self.end_headers()
51
+ self.wfile.write(json.dumps(data, ensure_ascii=False).encode())
52
+
53
+ def do_GET(self):
54
+ daemon = self.server.daemon
55
+
56
+ if self.path == "/health":
57
+ self.send_json(200, {"status": "ok", "daemon": "hermes", "version": "2.3.2"})
58
+ elif self.path == "/info":
59
+ self.send_json(200, {
60
+ "daemon": "hermes",
61
+ "version": "2.3.2",
62
+ "tasks": len(daemon.tasks),
63
+ "skills": len(daemon.skills),
64
+ "goals": len(daemon.goals),
65
+ "analytics": daemon.analytics
66
+ })
67
+ elif self.path == "/tasks":
68
+ self.send_json(200, {"tasks": daemon.tasks})
69
+ elif self.path == "/skills":
70
+ self.send_json(200, {"skills": daemon.skills})
71
+ elif self.path == "/goals":
72
+ self.send_json(200, {"goals": daemon.goals})
73
+ elif self.path == "/analytics":
74
+ self.send_json(200, daemon.analytics)
75
+ elif self.path == "/capabilities":
76
+ self.send_json(200, {
77
+ "endpoints": [
78
+ {"method": "GET", "path": "/health", "desc": "健康检查"},
79
+ {"method": "GET", "path": "/info", "desc": "守护进程信息"},
80
+ {"method": "GET", "path": "/tasks", "desc": "任务列表"},
81
+ {"method": "POST", "path": "/task", "desc": "添加任务", "body": {"task": "任务描述"}},
82
+ {"method": "GET", "path": "/skills", "desc": "技能列表"},
83
+ {"method": "POST", "path": "/skill", "desc": "添加技能", "body": {"name": "名称", "content": "内容"}},
84
+ {"method": "GET", "path": "/goals", "desc": "目标列表"},
85
+ {"method": "POST", "path": "/goal", "desc": "设置目标", "body": {"description": "目标", "milestones": ["阶段1", "阶段2"]}},
86
+ {"method": "PUT", "path": "/goal/progress", "desc": "更新进度", "body": {"id": "目标ID", "progress": 0.5}},
87
+ {"method": "GET", "path": "/analytics", "desc": "使用统计"},
88
+ {"method": "POST", "path": "/execute", "desc": "执行命令", "body": {"command": "ls -la"}},
89
+ {"method": "POST", "path": "/chat", "desc": "AI 对话", "body": {"message": "你好"}},
90
+ ]
91
+ })
92
+ else:
93
+ self.send_json(404, {"error": "Not Found", "hint": "访问 /capabilities 查看所有端点"})
94
+
95
+ def do_POST(self):
96
+ daemon = self.server.daemon
97
+ length = int(self.headers.get("Content-Length", 0))
98
+ body = self.rfile.read(length).decode() if length > 0 else "{}"
99
+ data = json.loads(body) if body else {}
100
+
101
+ if self.path == "/task":
102
+ task_id = f"task-{int(time.time())}"
103
+ daemon.tasks.append({
104
+ "id": task_id,
105
+ "task": data.get("task", ""),
106
+ "status": "pending",
107
+ "created_at": time.strftime("%Y-%m-%d %H:%M")
108
+ })
109
+ self.send_json(200, {"id": task_id, "status": "queued"})
110
+
111
+ elif self.path == "/skill":
112
+ skill_id = f"skill-{int(time.time())}"
113
+ daemon.skills.append({
114
+ "id": skill_id,
115
+ "name": data.get("name", ""),
116
+ "content": data.get("content", ""),
117
+ "tags": data.get("tags", []),
118
+ "created_at": time.strftime("%Y-%m-%d %H:%M")
119
+ })
120
+ self.send_json(200, {"id": skill_id, "status": "added"})
121
+
122
+ elif self.path == "/goal":
123
+ goal_id = f"goal-{int(time.time())}"
124
+ daemon.goals.append({
125
+ "id": goal_id,
126
+ "description": data.get("description", ""),
127
+ "milestones": data.get("milestones", []),
128
+ "progress": 0,
129
+ "status": "active",
130
+ "created_at": time.strftime("%Y-%m-%d %H:%M")
131
+ })
132
+ self.send_json(200, {"id": goal_id, "status": "created"})
133
+
134
+ elif self.path == "/execute":
135
+ cmd = data.get("command", "")
136
+ try:
137
+ result = subprocess.run(cmd, shell=True, capture_output=True, text=True, timeout=30)
138
+ self.send_json(200, {
139
+ "output": result.stdout,
140
+ "error": result.stderr,
141
+ "returncode": result.returncode,
142
+ "duration": f"{result.returncode}s"
143
+ })
144
+ except Exception as e:
145
+ self.send_json(500, {"error": str(e)})
146
+
147
+ elif self.path == "/chat":
148
+ message = data.get("message", "")
149
+ daemon.analytics["requests"] += 1
150
+ self.send_json(200, {
151
+ "reply": f"[Hermes] 收到消息: {message[:50]}...",
152
+ "task_id": f"task-{int(time.time())}",
153
+ "model": "hermes-ai"
154
+ })
155
+
156
+ elif self.path == "/analytics":
157
+ daemon.analytics["requests"] += data.get("requests", 0)
158
+ daemon.analytics["tokens"] += data.get("tokens", 0)
159
+ daemon.analytics["cost"] += data.get("cost", 0.0)
160
+ self.send_json(200, {"status": "recorded", "analytics": daemon.analytics})
161
+
162
+ else:
163
+ self.send_json(404, {"error": "Not Found"})
164
+
165
+ def do_PUT(self):
166
+ daemon = self.server.daemon
167
+ length = int(self.headers.get("Content-Length", 0))
168
+ body = self.rfile.read(length).decode() if length > 0 else "{}"
169
+ data = json.loads(body) if body else {}
170
+
171
+ if self.path == "/goal/progress":
172
+ goal_id = data.get("id")
173
+ for goal in daemon.goals:
174
+ if goal.get("id") == goal_id:
175
+ goal["progress"] = data.get("progress", 0)
176
+ goal["updated_at"] = time.strftime("%Y-%m-%d %H:%M")
177
+ self.send_json(200, {"status": "updated", "goal": goal})
178
+ return
179
+ self.send_json(404, {"error": "Goal not found"})
180
+ else:
181
+ self.send_json(404, {"error": "Not Found"})
182
+
183
+
184
+ def main():
185
+ import argparse
186
+ parser = argparse.ArgumentParser(description="Hermes 守护进程")
187
+ parser.add_argument("--port", type=int, default=8765)
188
+ parser.add_argument("--host", default="127.0.0.1")
189
+ args = parser.parse_args()
190
+
191
+ daemon = HermesDaemon(port=args.port, host=args.host)
192
+ try:
193
+ daemon.start()
194
+ except KeyboardInterrupt:
195
+ daemon.stop()
196
+
197
+
198
+ if __name__ == "__main__":
199
+ main()
@@ -38,7 +38,7 @@ class AppState:
38
38
  self.security = SecurityManager(self.lang, cfg)
39
39
 
40
40
  # MCP 法宝管理器
41
- self.mcp = MCPManager(cfg)
41
+ self.mcp = MCPManager()
42
42
 
43
43
  # 运行时消息与上下文
44
44
  self.messages = []
@@ -42,6 +42,8 @@ def _sync_manual_to_workspace(vfs):
42
42
 
43
43
  from fr_cli.repl.commands import (
44
44
  _cmd_exit,
45
+ _cmd_shell,
46
+ _cmd_hermes_daemon,
45
47
  _cmd_help,
46
48
  _cmd_model,
47
49
  _cmd_key,
@@ -101,6 +103,8 @@ from fr_cli.repl.commands import (
101
103
 
102
104
  _COMMAND_ROUTES = {
103
105
  "/exit": _cmd_exit,
106
+ "/shell": _cmd_shell,
107
+ "/hermes": _cmd_hermes_daemon,
104
108
  "/quit": _cmd_exit,
105
109
  "/help": _cmd_help,
106
110
  "/model": _cmd_model,
@@ -9,6 +9,7 @@ from fr_cli.ui.ui import (
9
9
  CYAN, RED, YELLOW, GREEN, DIM, RESET,
10
10
  print_bye
11
11
  )
12
+ from fr_cli.agent.shell_mode import ShellMode
12
13
  from fr_cli.memory.history import save_sess, load_sess, del_sess, get_sessions
13
14
  from fr_cli.memory.context import load_context, extract_recent_turns, build_context_summary, save_context
14
15
  from fr_cli.memory.session import (
@@ -70,6 +71,8 @@ def _print_help(state, topic):
70
71
  print(f" {T('help_sess', lang)} /save /load /del /undo")
71
72
  print(f" {DIM} 【岁月】 /session_list | /session_load <编号> | /session_del <编号>{RESET}")
72
73
  print(f" {DIM} 【本命元神】 /master on|off|status — 启用自我进化型主控元神{RESET}")
74
+ print(f" {DIM} 【蜗壳】 /shell — 进入 Shell 模式,直接执行命令{RESET}")
75
+ print(f" {DIM} 【Hermes】 /hermes start|stop|status — 守护进程{RESET}")
73
76
  print(f" {T('help_plugin', lang)} /skills (自动进化)")
74
77
  print(f" {DIM} 【悟道】 /mode <direct|cot|tot|react> — 切换 AI 推演模式{RESET}")
75
78
  print(f" {T('help_extra', lang)} /mail_* /cron_* /web /fetch /disk_* /see")
@@ -104,6 +107,36 @@ def _cmd_exit(state, parts):
104
107
  return True
105
108
 
106
109
 
110
+ def _cmd_shell(state, parts):
111
+ """进入 Shell 模式"""
112
+ from fr_cli.agent.shell_mode import get_shell_manager
113
+
114
+ shell_mgr = get_shell_manager()
115
+ shell_mgr.current_mode = ShellMode.SHELL if shell_mgr.current_mode == ShellMode.AGENT else ShellMode.AGENT
116
+
117
+ if shell_mgr.current_mode == ShellMode.SHELL:
118
+ print(f"{GREEN}🆗 进入 Shell 模式 - 直接执行命令,输入 exit 返回{RESET}")
119
+ while shell_mgr.current_mode == ShellMode.SHELL:
120
+ try:
121
+ cmd = input("(shell) $ ").strip()
122
+ if not cmd:
123
+ continue
124
+ if cmd in ['exit', 'quit', 'q']:
125
+ print(f"{YELLOW}退出 Shell 模式{RESET}")
126
+ break
127
+ output, code = shell_mgr.execute_command(cmd)
128
+ print(output)
129
+ if code != 0:
130
+ print(f"[exit {code}]")
131
+ except (EOFError, KeyboardInterrupt):
132
+ print(f"\n{YELLOW}退出 Shell 模式{RESET}")
133
+ break
134
+ shell_mgr.current_mode = ShellMode.AGENT
135
+ else:
136
+ print(f"{GREEN}切换回 Agent 模式{RESET}")
137
+ return False
138
+
139
+
107
140
  def _cmd_help(state, parts):
108
141
  arg1 = parts[1] if len(parts) > 1 else ""
109
142
  _print_help(state, arg1.lower())
@@ -530,6 +563,43 @@ def _cmd_update(state, parts):
530
563
  return False
531
564
 
532
565
 
566
+ def _cmd_hermes_daemon(state, parts):
567
+ """Hermes 守护进程命令"""
568
+ arg1 = parts[1] if len(parts) > 1 else ""
569
+
570
+ if arg1 == "start":
571
+ port = int(parts[2]) if len(parts) > 2 and parts[2].isdigit() else 8765
572
+ try:
573
+ from fr_cli.agent.hermes_daemon import HermesDaemon
574
+ import threading
575
+ daemon = HermesDaemon(port=port)
576
+ state.hermes_daemon = daemon
577
+ t = threading.Thread(target=daemon.start, daemon=True)
578
+ t.start()
579
+ print(f"{GREEN}🧚 Hermes 守护进程已启动: http://127.0.0.1:{port}{RESET}")
580
+ except Exception as e:
581
+ print(f"{RED}启动失败: {e}{RESET}")
582
+
583
+ elif arg1 == "stop":
584
+ if hasattr(state, "hermes_daemon") and state.hermes_daemon:
585
+ state.hermes_daemon.running = False
586
+ print(f"{GREEN}🛑 守护进程已停止{RESET}")
587
+ else:
588
+ print(f"{YELLOW}守护进程未运行{RESET}")
589
+
590
+ elif arg1 == "status":
591
+ if hasattr(state, "hermes_daemon") and state.hermes_daemon:
592
+ print(f"{CYAN}🧚 守护进程运行中{RESET}")
593
+ print(f" 端口: {state.hermes_daemon.port}")
594
+ else:
595
+ print(f"{DIM}守护进程未运行{RESET}")
596
+
597
+ else:
598
+ print(f"{DIM}用法: /hermes start [port] | /hermes stop | /hermes status{RESET}")
599
+ print(f"{DIM}示例: /hermes start 8765{RESET}")
600
+ return False
601
+
602
+
533
603
  def _cmd_agent_server(state, parts):
534
604
  from fr_cli.agent.server import AgentHTTPServer
535
605
  arg1 = parts[1] if len(parts) > 1 else ""
@@ -183,4 +183,6 @@ def get_mcp_manager() -> MCPServerManager:
183
183
 
184
184
  def load_from_config_file(config_file: str) -> MCPServerManager:
185
185
  """从配置文件加载 MCP 服务器"""
186
- return MCPServerManager.from_config_file(config_file)
186
+ return MCPServerManager.from_config_file(config_file)
187
+
188
+ MCPManager = MCPServerManager
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fr-cli
3
- Version: 2.3.1
3
+ Version: 2.3.3
4
4
  Summary: 凡人打字机 - 支持多模型(Zhipu/DeepSeek/Kimi/Qwen/StepFun/MiniMax/Spark/Doubao/MiMo)的终极全能终端工具
5
5
  Author: FANREN CLI Author
6
6
  License-Expression: MIT
@@ -23,6 +23,7 @@ fr_cli/agent/executor.py
23
23
  fr_cli/agent/gateway.py
24
24
  fr_cli/agent/generator.py
25
25
  fr_cli/agent/hermes.py
26
+ fr_cli/agent/hermes_daemon.py
26
27
  fr_cli/agent/image_and_parallel.py
27
28
  fr_cli/agent/manager.py
28
29
  fr_cli/agent/master.py
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "fr-cli"
7
- version = "2.3.1"
7
+ version = "2.3.3"
8
8
  description = "凡人打字机 - 支持多模型(Zhipu/DeepSeek/Kimi/Qwen/StepFun/MiniMax/Spark/Doubao/MiMo)的终极全能终端工具"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.8"
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes