chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,30 @@
|
||||
"""
|
||||
ga_cli - GenericAgent CLI 命令包
|
||||
|
||||
作为包导入时,自动从根 ga.py 加载核心类(GenericAgentHandler 等)
|
||||
以 python -m ga_cli 运行时,进入 CLI 命令模式
|
||||
"""
|
||||
import importlib.util, sys, os
|
||||
|
||||
# ── 确保项目根在 sys.path(ga.py 依赖 agent_loop 等)──
|
||||
_PROJECT_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
if _PROJECT_ROOT not in sys.path:
|
||||
sys.path.insert(0, _PROJECT_ROOT)
|
||||
|
||||
# ── 从根 ga.py 重新导出核心符号(agentmain.py 等依赖)──
|
||||
_root_ga = os.path.join(_PROJECT_ROOT, 'ga.py')
|
||||
if os.path.exists(_root_ga):
|
||||
_spec = importlib.util.spec_from_file_location("_ga_root", _root_ga)
|
||||
_mod = importlib.util.module_from_spec(_spec)
|
||||
sys.modules['_ga_root'] = _mod
|
||||
_spec.loader.exec_module(_mod)
|
||||
# 公开导出
|
||||
_EXPORT_NAMES = [
|
||||
'GenericAgentHandler', 'smart_format', 'get_global_memory',
|
||||
'format_error', 'consume_file', 'code_run', 'script_dir',
|
||||
'BaseHandler', 'StepOutcome',
|
||||
]
|
||||
for _name in _EXPORT_NAMES:
|
||||
if hasattr(_mod, _name):
|
||||
globals()[_name] = getattr(_mod, _name)
|
||||
__all__ = [n for n in _EXPORT_NAMES if hasattr(_mod, n)]
|
||||
@@ -0,0 +1,3 @@
|
||||
"""ga_cli 命令行入口"""
|
||||
from ga_cli.cli import main
|
||||
main()
|
||||
+225
@@ -0,0 +1,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()
|
||||
@@ -0,0 +1,62 @@
|
||||
@echo off
|
||||
chcp 65001 >nul
|
||||
:: ga-cli-install.cmd - 将 GenericAgent 命令注册到系统 PATH
|
||||
:: 运行一次后,即可在任意位置敲 ga <command>
|
||||
:: 建议以管理员身份运行
|
||||
|
||||
cd /d "%~dp0.."
|
||||
set "TARGET_DIR=%CD%"
|
||||
|
||||
echo.
|
||||
echo ╔══════════════════════════════════════════╗
|
||||
echo ║ GenericAgent 命令行安装向导 ║
|
||||
echo ╚══════════════════════════════════════════╝
|
||||
echo.
|
||||
echo 项目目录: %TARGET_DIR%
|
||||
echo.
|
||||
|
||||
REM 检查是否已在 PATH 中
|
||||
echo %%PATH%% | find /I "%TARGET_DIR%" >nul
|
||||
if not errorlevel 1 (
|
||||
echo [✓] 项目目录已在 PATH 中
|
||||
echo 你可以在任意位置敲: ga list
|
||||
pause
|
||||
exit /b 0
|
||||
)
|
||||
|
||||
set /p "YN=是否将项目目录添加到系统 PATH?(Y/n): "
|
||||
if /I "%YN%"=="n" (
|
||||
echo 已取消
|
||||
pause
|
||||
exit /b 0
|
||||
)
|
||||
|
||||
REM 尝试添加(需管理员权限)
|
||||
for /f "skip=2 tokens=3*" %%a in ('reg query "HKCU\Environment" /v PATH 2^>nul') do set "CURRENT_PATH=%%a %%b"
|
||||
if not defined CURRENT_PATH (
|
||||
reg add "HKCU\Environment" /v PATH /t REG_EXPAND_SZ /d "%TARGET_DIR%" /f >nul
|
||||
) else (
|
||||
rem 避免重复添加
|
||||
echo %%CURRENT_PATH%% | find /I "%TARGET_DIR%" >nul
|
||||
if errorlevel 1 (
|
||||
reg add "HKCU\Environment" /v PATH /t REG_EXPAND_SZ /d "%TARGET_DIR%;%CURRENT_PATH%" /f >nul
|
||||
)
|
||||
)
|
||||
if %errorlevel% equ 0 (
|
||||
echo [✓] PATH 添加成功
|
||||
) else (
|
||||
echo [!] 添加失败,请以管理员身份运行
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
echo.
|
||||
echo ────────────────────────────────────────────
|
||||
echo 安装完成!
|
||||
echo.
|
||||
echo 现在你可以:
|
||||
echo - 打开新的终端窗口
|
||||
echo - 在项目目录敲: ga list
|
||||
echo - 在任意位置敲: ga gui / ga web / ga hub ...
|
||||
echo ────────────────────────────────────────────
|
||||
pause
|
||||
@@ -0,0 +1,5 @@
|
||||
@echo off
|
||||
cd /d "%~dp0.."
|
||||
set "PY=.venv\Scripts\python.exe"
|
||||
if not exist "%PY%" set "PY=python"
|
||||
"%PY%" -m ga_cli %*
|
||||
Reference in New Issue
Block a user