fr-cli 2.3.2__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.
- {fr_cli-2.3.2/fr_cli.egg-info → fr_cli-2.3.3}/PKG-INFO +1 -1
- fr_cli-2.3.3/fr_cli/agent/hermes_daemon.py +199 -0
- {fr_cli-2.3.2 → fr_cli-2.3.3}/fr_cli/core/core.py +1 -1
- {fr_cli-2.3.2 → fr_cli-2.3.3}/fr_cli/main.py +4 -0
- {fr_cli-2.3.2 → fr_cli-2.3.3}/fr_cli/repl/commands.py +70 -0
- {fr_cli-2.3.2 → fr_cli-2.3.3/fr_cli.egg-info}/PKG-INFO +1 -1
- {fr_cli-2.3.2 → fr_cli-2.3.3}/fr_cli.egg-info/SOURCES.txt +1 -0
- {fr_cli-2.3.2 → fr_cli-2.3.3}/pyproject.toml +1 -1
- {fr_cli-2.3.2 → fr_cli-2.3.3}/LICENSE +0 -0
- {fr_cli-2.3.2 → fr_cli-2.3.3}/MANIFEST.in +0 -0
- {fr_cli-2.3.2 → fr_cli-2.3.3}/README.md +0 -0
- {fr_cli-2.3.2 → fr_cli-2.3.3}/fr_cli/README.md +0 -0
- {fr_cli-2.3.2 → fr_cli-2.3.3}/fr_cli/WEAPON.MD +0 -0
- {fr_cli-2.3.2 → fr_cli-2.3.3}/fr_cli/__init__.py +0 -0
- {fr_cli-2.3.2 → fr_cli-2.3.3}/fr_cli/addon/plugin.py +0 -0
- {fr_cli-2.3.2 → fr_cli-2.3.3}/fr_cli/agent/__init__.py +0 -0
- {fr_cli-2.3.2 → fr_cli-2.3.3}/fr_cli/agent/a2a.py +0 -0
- {fr_cli-2.3.2 → fr_cli-2.3.3}/fr_cli/agent/acp.py +0 -0
- {fr_cli-2.3.2 → fr_cli-2.3.3}/fr_cli/agent/builtins/__init__.py +0 -0
- {fr_cli-2.3.2 → fr_cli-2.3.3}/fr_cli/agent/builtins/_utils.py +0 -0
- {fr_cli-2.3.2 → fr_cli-2.3.3}/fr_cli/agent/builtins/db.py +0 -0
- {fr_cli-2.3.2 → fr_cli-2.3.3}/fr_cli/agent/builtins/local.py +0 -0
- {fr_cli-2.3.2 → fr_cli-2.3.3}/fr_cli/agent/builtins/powerful_agent_template.py +0 -0
- {fr_cli-2.3.2 → fr_cli-2.3.3}/fr_cli/agent/builtins/rag.py +0 -0
- {fr_cli-2.3.2 → fr_cli-2.3.3}/fr_cli/agent/builtins/rag_watcher_daemon.py +0 -0
- {fr_cli-2.3.2 → fr_cli-2.3.3}/fr_cli/agent/builtins/remote.py +0 -0
- {fr_cli-2.3.2 → fr_cli-2.3.3}/fr_cli/agent/builtins/spider.py +0 -0
- {fr_cli-2.3.2 → fr_cli-2.3.3}/fr_cli/agent/client.py +0 -0
- {fr_cli-2.3.2 → fr_cli-2.3.3}/fr_cli/agent/coding_helper.py +0 -0
- {fr_cli-2.3.2 → fr_cli-2.3.3}/fr_cli/agent/context_files.py +0 -0
- {fr_cli-2.3.2 → fr_cli-2.3.3}/fr_cli/agent/executor.py +0 -0
- {fr_cli-2.3.2 → fr_cli-2.3.3}/fr_cli/agent/gateway.py +0 -0
- {fr_cli-2.3.2 → fr_cli-2.3.3}/fr_cli/agent/generator.py +0 -0
- {fr_cli-2.3.2 → fr_cli-2.3.3}/fr_cli/agent/hermes.py +0 -0
- {fr_cli-2.3.2 → fr_cli-2.3.3}/fr_cli/agent/image_and_parallel.py +0 -0
- {fr_cli-2.3.2 → fr_cli-2.3.3}/fr_cli/agent/manager.py +0 -0
- {fr_cli-2.3.2 → fr_cli-2.3.3}/fr_cli/agent/master.py +0 -0
- {fr_cli-2.3.2 → fr_cli-2.3.3}/fr_cli/agent/master_prompt.py +0 -0
- {fr_cli-2.3.2 → fr_cli-2.3.3}/fr_cli/agent/personality.py +0 -0
- {fr_cli-2.3.2 → fr_cli-2.3.3}/fr_cli/agent/plugin_system.py +0 -0
- {fr_cli-2.3.2 → fr_cli-2.3.3}/fr_cli/agent/remote.py +0 -0
- {fr_cli-2.3.2 → fr_cli-2.3.3}/fr_cli/agent/server.py +0 -0
- {fr_cli-2.3.2 → fr_cli-2.3.3}/fr_cli/agent/shell_mode.py +0 -0
- {fr_cli-2.3.2 → fr_cli-2.3.3}/fr_cli/agent/skills.py +0 -0
- {fr_cli-2.3.2 → fr_cli-2.3.3}/fr_cli/agent/workflow.py +0 -0
- {fr_cli-2.3.2 → fr_cli-2.3.3}/fr_cli/agent/workflow_system.py +0 -0
- {fr_cli-2.3.2 → fr_cli-2.3.3}/fr_cli/breakthrough/update.py +0 -0
- {fr_cli-2.3.2 → fr_cli-2.3.3}/fr_cli/command/__init__.py +0 -0
- {fr_cli-2.3.2 → fr_cli-2.3.3}/fr_cli/command/executor.py +0 -0
- {fr_cli-2.3.2 → fr_cli-2.3.3}/fr_cli/command/registry.py +0 -0
- {fr_cli-2.3.2 → fr_cli-2.3.3}/fr_cli/command/security.py +0 -0
- {fr_cli-2.3.2 → fr_cli-2.3.3}/fr_cli/conf/config.py +0 -0
- {fr_cli-2.3.2 → fr_cli-2.3.3}/fr_cli/conf/wizard.py +0 -0
- {fr_cli-2.3.2 → fr_cli-2.3.3}/fr_cli/core/chat.py +0 -0
- {fr_cli-2.3.2 → fr_cli-2.3.3}/fr_cli/core/intent.py +0 -0
- {fr_cli-2.3.2 → fr_cli-2.3.3}/fr_cli/core/llm.py +0 -0
- {fr_cli-2.3.2 → fr_cli-2.3.3}/fr_cli/core/model_factory.py +0 -0
- {fr_cli-2.3.2 → fr_cli-2.3.3}/fr_cli/core/recommender.py +0 -0
- {fr_cli-2.3.2 → fr_cli-2.3.3}/fr_cli/core/stream.py +0 -0
- {fr_cli-2.3.2 → fr_cli-2.3.3}/fr_cli/core/sysmon.py +0 -0
- {fr_cli-2.3.2 → fr_cli-2.3.3}/fr_cli/core/thinking.py +0 -0
- {fr_cli-2.3.2 → fr_cli-2.3.3}/fr_cli/gatekeeper/__init__.py +0 -0
- {fr_cli-2.3.2 → fr_cli-2.3.3}/fr_cli/gatekeeper/daemon.py +0 -0
- {fr_cli-2.3.2 → fr_cli-2.3.3}/fr_cli/gatekeeper/manager.py +0 -0
- {fr_cli-2.3.2 → fr_cli-2.3.3}/fr_cli/lang/i18n.py +0 -0
- {fr_cli-2.3.2 → fr_cli-2.3.3}/fr_cli/memory/context.py +0 -0
- {fr_cli-2.3.2 → fr_cli-2.3.3}/fr_cli/memory/history.py +0 -0
- {fr_cli-2.3.2 → fr_cli-2.3.3}/fr_cli/memory/session.py +0 -0
- {fr_cli-2.3.2 → fr_cli-2.3.3}/fr_cli/repl/__init__.py +0 -0
- {fr_cli-2.3.2 → fr_cli-2.3.3}/fr_cli/security/security.py +0 -0
- {fr_cli-2.3.2 → fr_cli-2.3.3}/fr_cli/ui/ui.py +0 -0
- {fr_cli-2.3.2 → fr_cli-2.3.3}/fr_cli/weapon/cron.py +0 -0
- {fr_cli-2.3.2 → fr_cli-2.3.3}/fr_cli/weapon/dataframe.py +0 -0
- {fr_cli-2.3.2 → fr_cli-2.3.3}/fr_cli/weapon/disk.py +0 -0
- {fr_cli-2.3.2 → fr_cli-2.3.3}/fr_cli/weapon/fs.py +0 -0
- {fr_cli-2.3.2 → fr_cli-2.3.3}/fr_cli/weapon/launcher.py +0 -0
- {fr_cli-2.3.2 → fr_cli-2.3.3}/fr_cli/weapon/loader.py +0 -0
- {fr_cli-2.3.2 → fr_cli-2.3.3}/fr_cli/weapon/mail.py +0 -0
- {fr_cli-2.3.2 → fr_cli-2.3.3}/fr_cli/weapon/mcp.py +0 -0
- {fr_cli-2.3.2 → fr_cli-2.3.3}/fr_cli/weapon/vision.py +0 -0
- {fr_cli-2.3.2 → fr_cli-2.3.3}/fr_cli/weapon/web.py +0 -0
- {fr_cli-2.3.2 → fr_cli-2.3.3}/fr_cli.egg-info/dependency_links.txt +0 -0
- {fr_cli-2.3.2 → fr_cli-2.3.3}/fr_cli.egg-info/entry_points.txt +0 -0
- {fr_cli-2.3.2 → fr_cli-2.3.3}/fr_cli.egg-info/requires.txt +0 -0
- {fr_cli-2.3.2 → fr_cli-2.3.3}/fr_cli.egg-info/top_level.txt +0 -0
- {fr_cli-2.3.2 → fr_cli-2.3.3}/setup.cfg +0 -0
- {fr_cli-2.3.2 → fr_cli-2.3.3}/tests/test_a2a_and_providers.py +0 -0
- {fr_cli-2.3.2 → fr_cli-2.3.3}/tests/test_integration_real.py +0 -0
- {fr_cli-2.3.2 → fr_cli-2.3.3}/tests/test_master_prompt_fix.py +0 -0
- {fr_cli-2.3.2 → fr_cli-2.3.3}/tests/test_model_config.py +0 -0
- {fr_cli-2.3.2 → fr_cli-2.3.3}/tests/test_new_features.py +0 -0
- {fr_cli-2.3.2 → fr_cli-2.3.3}/tests/test_new_providers.py +0 -0
|
@@ -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()
|
|
@@ -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 ""
|
|
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
|
|
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
|