-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Expand file tree
/
Copy pathcli.py
More file actions
225 lines (188 loc) · 6.92 KB
/
Copy pathcli.py
File metadata and controls
225 lines (188 loc) · 6.92 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
"""
ga_cli/cli.py - GenericAgent 命令行分发系统
通过 python -m ga_cli <命令> 或 ga <命令> 调用
"""
import os, sys, subprocess, argparse, textwrap
# Windows GBK 终端兼容
if sys.platform == "win32" and sys.stdout.encoding and sys.stdout.encoding.lower() in ("gbk", "gb2312"):
sys.stdout.reconfigure(errors="replace") if hasattr(sys.stdout, "reconfigure") else None
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
PROJECT_DIR = os.path.dirname(SCRIPT_DIR)
def _frontends():
return os.path.join(PROJECT_DIR, "frontends")
def _reflect():
return os.path.join(PROJECT_DIR, "reflect")
def launch_frontend(cmd_parts, args=None):
"""启动前端/工具进程"""
full_cmd = []
for part in cmd_parts:
part = part.replace("{PROJECT_DIR}", PROJECT_DIR)
part = part.replace("{FRONTENDS}", _frontends())
part = part.replace("{REFLECT}", _reflect())
full_cmd.append(part)
# 插入额外参数
if args:
full_cmd.extend(args)
print(f"🚀 {' '.join(full_cmd)}")
sys.stdout.flush()
os.chdir(PROJECT_DIR)
proc = subprocess.Popen(full_cmd)
try:
proc.wait()
except KeyboardInterrupt:
proc.terminate()
sys.exit(0)
COMMANDS = {
"gui": {
"help": "启动桌面GUI (qtapp)",
"desc": "启动基于 PyQt5 的完整桌面聊天界面(气泡代码高亮、文件拖拽、历史搜索)",
"cmd": ["python", "{FRONTENDS}/qtapp.py"],
},
"configure": {
"help": "运行初始配置向导 (configure_mykey.py)",
"desc": "首次安装后配置 API Key、模型参数等基础设置",
"cmd": ["python", "{PROJECT_DIR}/assets/configure_mykey.py"],
},
"hub": {
"help": "启动 Hub 管理器 (launcher)",
"desc": "启动 hub 前端管理面板(系统托盘 + 浏览器界面)",
"cmd": ["python", "{PROJECT_DIR}/hub.pyw"],
},
"tui": {
"help": "启动终端 TUI (tuiapp)",
"desc": "启动终端图形界面(Textual),适合纯终端环境或 SSH",
"cmd": ["python", "{FRONTENDS}/tuiapp.py"],
},
"tui2": {
"help": "启动终端 TUI v2 (tuiapp_v2)",
"desc": "启动增强版终端图形界面(Textual v2),更多功能更好的体验",
"cmd": ["python", "{FRONTENDS}/tuiapp_v2.py"],
},
"cli": {
"help": "启动 CLI 对话 (agentmain)",
"desc": "启动命令行交互对话模式,最轻量的使用方式",
"cmd": ["python", "{PROJECT_DIR}/agentmain.py"],
},
"launch": {
"help": "启动 webview 桌面壳 (launch.pyw)",
"desc": "以原生窗口形式包装 stapp Web 界面(基于 pywebview)",
"cmd": ["python", "{PROJECT_DIR}/launch.pyw"],
},
"status": {
"help": "检查运行状态",
"desc": "检查当前是否已有 GenericAgent 进程在运行",
"cmd": None,
"internal": True,
},
"update": {
"help": "更新项目 (git pull + pip install)",
"desc": "从 Git 拉取最新代码并更新依赖",
"cmd": None,
"internal": True,
},
"list": {
"help": "列出所有可用前端/服务",
"desc": "显示所有注册的命令",
"cmd": None,
"internal": True,
},
}
def cmd_list():
"""展示所有可用命令"""
print()
frontend_cmds = [(k, v) for k, v in sorted(COMMANDS.items()) if v["cmd"] is not None]
internal_cmds = [(k, v) for k, v in sorted(COMMANDS.items()) if v["cmd"] is None]
print(f" {'命令':20s} {'说明'}")
print(f" {'━'*20} {'━'*40}")
for name, info in frontend_cmds:
print(f" {name:20s} {info.get('help', info['desc'][:40])}")
print()
for name, info in internal_cmds:
print(f" {name:20s} {info.get('help', info['desc'][:40])}")
print()
def cmd_status():
"""检查进程状态"""
import psutil
running = [p for p in psutil.process_iter(['pid', 'name', 'cmdline'])
if p.info['cmdline'] and any('agentmain' in c for c in p.info['cmdline'])]
if running:
print(f"🟢 运行中: {len(running)} 个进程")
for p in running:
print(f" PID {p.info['pid']} — {' '.join(p.info['cmdline'][:3])}")
else:
print("⚫ GenericAgent 进程未运行")
def cmd_update():
"""git pull + pip install"""
os.chdir(PROJECT_DIR)
print("🔄 git pull...")
r = subprocess.run(["git", "pull"], capture_output=True, text=True)
print(r.stdout)
if r.returncode != 0:
print(r.stderr)
print("📦 pip install...")
r2 = subprocess.run([sys.executable, "-m", "pip", "install", "-e", "."],
capture_output=True, text=True)
print(r2.stdout[-500:] if r2.stdout else "")
if r2.returncode != 0:
print(r2.stderr[-500:])
def main():
parser = argparse.ArgumentParser(
prog="ga",
description="GenericAgent 全局命令入口",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog=textwrap.dedent("""\
示例:
ga gui 启动桌面 GUI
ga web 启动 Web 增强版
ga web --native 启动 Web 基础版(桌面壳)
ga tui 启动终端 TUI (v1)
ga tui2 启动终端 TUI (v2 增强版)
ga pet 启动桌面宠物 v2
ga launch 启动 webview 桌面壳
ga list 列出所有命令
"""),
)
parser.add_argument("command", nargs="?", help="命令名")
parser.add_argument("args", nargs="*", help="子命令参数")
parser.add_argument("-v", "--version", action="store_true", help="显示版本")
args, unknown = parser.parse_known_args()
if args.version:
print("GenericAgent v0.1.0")
return
cmd = args.command
if not cmd or cmd == "help":
parser.print_help()
print("\n--- 命令列表 ---")
cmd_list()
return
if cmd == "list":
cmd_list()
return
if cmd == "status":
cmd_status()
return
if cmd == "update":
cmd_update()
return
if cmd not in COMMANDS:
print(f"❌ 未知命令: {cmd}")
print(f" 使用 'ga list' 查看可用命令")
sys.exit(1)
info = COMMANDS[cmd]
# 内置命令走内部逻辑
if info.get("internal"):
print(f"❌ 命令 {cmd} 没有配置启动命令")
sys.exit(1)
extra = list(args.args) + unknown
# === 处理命令特有 flags ===
cmd_parts = list(info["cmd"])
# 处理 flags (如 --native)
flags = info.get("flags", {})
for flag_name, flag_info in flags.items():
if flag_name in extra:
cmd_parts = list(flag_info["cmd"])
extra.remove(flag_name)
break
launch_frontend(cmd_parts, extra if extra else None)
if __name__ == "__main__":
main()