Files
lsdefine--genericagent/ga_cli/cli.py
T
2026-07-13 13:04:19 +08:00

226 lines
6.9 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
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()