2025-12-15 02:08:06 +08:00
|
|
|
|
"""CLI 入口:解析参数并调度 decide 功能。"""
|
|
|
|
|
|
|
|
|
|
|
|
from __future__ import annotations
|
|
|
|
|
|
|
|
|
|
|
|
import json
|
2025-12-15 04:01:12 +08:00
|
|
|
|
import subprocess
|
2025-12-15 02:08:06 +08:00
|
|
|
|
import sys
|
2025-12-15 04:01:12 +08:00
|
|
|
|
import time
|
2025-12-15 02:08:06 +08:00
|
|
|
|
from pathlib import Path
|
|
|
|
|
|
|
2025-12-15 04:01:12 +08:00
|
|
|
|
from aide.core import output
|
2025-12-15 02:08:06 +08:00
|
|
|
|
from aide.decide.errors import DecideError
|
|
|
|
|
|
from aide.decide.storage import DecideStorage
|
|
|
|
|
|
from aide.decide.types import DecideInput
|
|
|
|
|
|
|
|
|
|
|
|
|
2025-12-15 04:01:12 +08:00
|
|
|
|
def cmd_decide_submit(file_path: str) -> bool:
|
|
|
|
|
|
"""从文件读取数据,启动后台 Web 服务。"""
|
2025-12-15 02:08:06 +08:00
|
|
|
|
root = Path.cwd()
|
|
|
|
|
|
storage = DecideStorage(root)
|
|
|
|
|
|
|
2025-12-15 04:01:12 +08:00
|
|
|
|
# 1. 读取 JSON 文件
|
|
|
|
|
|
json_file = Path(file_path)
|
|
|
|
|
|
if not json_file.is_absolute():
|
|
|
|
|
|
json_file = root / json_file
|
|
|
|
|
|
|
|
|
|
|
|
if not json_file.exists():
|
|
|
|
|
|
_print_error(f"文件不存在: {file_path}")
|
|
|
|
|
|
return False
|
|
|
|
|
|
|
2025-12-15 02:08:06 +08:00
|
|
|
|
try:
|
2025-12-15 04:01:12 +08:00
|
|
|
|
raw = json.loads(json_file.read_text(encoding="utf-8"))
|
2025-12-15 02:08:06 +08:00
|
|
|
|
except json.JSONDecodeError as exc:
|
|
|
|
|
|
_print_error(f"JSON 解析失败: {exc}", "检查 JSON 格式是否正确")
|
|
|
|
|
|
return False
|
|
|
|
|
|
|
2025-12-15 04:01:12 +08:00
|
|
|
|
# 2. 验证数据格式
|
2025-12-15 02:08:06 +08:00
|
|
|
|
try:
|
|
|
|
|
|
decide_input = DecideInput.from_dict(raw)
|
|
|
|
|
|
except DecideError as exc:
|
|
|
|
|
|
_print_error(f"数据验证失败: {exc}", "检查必填字段是否完整")
|
|
|
|
|
|
return False
|
|
|
|
|
|
|
2025-12-15 04:01:12 +08:00
|
|
|
|
# 3. 保存到 pending.json
|
2025-12-15 02:08:06 +08:00
|
|
|
|
try:
|
|
|
|
|
|
storage.save_pending(decide_input)
|
|
|
|
|
|
except DecideError as exc:
|
|
|
|
|
|
_print_error(str(exc))
|
|
|
|
|
|
return False
|
|
|
|
|
|
|
2025-12-15 04:01:12 +08:00
|
|
|
|
# 4. 启动后台服务
|
|
|
|
|
|
return _start_daemon(root, storage)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _start_daemon(root: Path, storage: DecideStorage) -> bool:
|
|
|
|
|
|
"""启动后台服务进程。"""
|
|
|
|
|
|
# 启动 daemon 进程
|
|
|
|
|
|
daemon_module = "aide.decide.daemon"
|
|
|
|
|
|
try:
|
|
|
|
|
|
subprocess.Popen(
|
|
|
|
|
|
[sys.executable, "-m", daemon_module, str(root)],
|
|
|
|
|
|
stdout=subprocess.DEVNULL,
|
|
|
|
|
|
stderr=subprocess.DEVNULL,
|
|
|
|
|
|
start_new_session=True, # 脱离父进程
|
|
|
|
|
|
)
|
|
|
|
|
|
except Exception as exc:
|
|
|
|
|
|
_print_error(f"启动后台服务失败: {exc}")
|
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
|
|
# 等待服务启动(检查状态文件)
|
|
|
|
|
|
for _ in range(50): # 最多等待 5 秒
|
|
|
|
|
|
time.sleep(0.1)
|
|
|
|
|
|
info = storage.load_server_info()
|
|
|
|
|
|
if info and "url" in info:
|
|
|
|
|
|
output.info("Web 服务已启动")
|
|
|
|
|
|
output.info(f"请访问: {info['url']}")
|
|
|
|
|
|
output.info("用户完成决策后执行 aide decide result 获取结果")
|
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
|
|
_print_error("服务启动超时", "请检查端口是否被占用")
|
|
|
|
|
|
return False
|
2025-12-15 02:08:06 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def cmd_decide_result() -> bool:
|
2025-12-15 04:01:12 +08:00
|
|
|
|
"""获取决策结果(服务在用户提交后自动关闭)。"""
|
2025-12-15 02:08:06 +08:00
|
|
|
|
root = Path.cwd()
|
|
|
|
|
|
storage = DecideStorage(root)
|
|
|
|
|
|
|
2025-12-15 04:01:12 +08:00
|
|
|
|
# 检查 pending
|
2025-12-15 02:08:06 +08:00
|
|
|
|
try:
|
|
|
|
|
|
pending = storage.load_pending()
|
|
|
|
|
|
except DecideError as exc:
|
|
|
|
|
|
_print_error(str(exc))
|
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
|
|
if pending is None:
|
2025-12-15 04:01:12 +08:00
|
|
|
|
_print_error("未找到待定项数据", "请先执行 aide decide submit <file>")
|
2025-12-15 02:08:06 +08:00
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
|
|
session_id = pending.meta.session_id if pending.meta else None
|
|
|
|
|
|
if not session_id:
|
2025-12-15 04:01:12 +08:00
|
|
|
|
_print_error("数据异常", "pending.json 缺少 session_id,请重新执行 aide decide submit")
|
2025-12-15 02:08:06 +08:00
|
|
|
|
return False
|
|
|
|
|
|
|
2025-12-15 04:01:12 +08:00
|
|
|
|
# 检查结果
|
2025-12-15 02:08:06 +08:00
|
|
|
|
try:
|
|
|
|
|
|
result = storage.load_result()
|
|
|
|
|
|
except DecideError as exc:
|
|
|
|
|
|
_print_error(str(exc))
|
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
|
|
if result is None:
|
2025-12-15 04:01:12 +08:00
|
|
|
|
# 检查服务是否还在运行
|
|
|
|
|
|
if storage.is_server_running():
|
2025-12-15 02:08:06 +08:00
|
|
|
|
_print_error("尚无决策结果", "请等待用户在 Web 界面完成操作")
|
2025-12-15 04:01:12 +08:00
|
|
|
|
else:
|
|
|
|
|
|
# 服务已关闭但没有结果,可能是超时或异常
|
|
|
|
|
|
_print_error("尚无决策结果", "服务可能已超时关闭,请重新执行 aide decide submit")
|
2025-12-15 02:08:06 +08:00
|
|
|
|
return False
|
|
|
|
|
|
|
2025-12-15 04:01:12 +08:00
|
|
|
|
# 输出结果
|
2025-12-15 02:08:06 +08:00
|
|
|
|
payload = json.dumps(result.to_dict(), ensure_ascii=False, separators=(",", ":"))
|
|
|
|
|
|
print(payload)
|
2025-12-15 04:01:12 +08:00
|
|
|
|
|
|
|
|
|
|
# 清理服务状态文件(如存在)
|
|
|
|
|
|
storage.clear_server_info()
|
2025-12-15 02:08:06 +08:00
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _print_error(message: str, suggestion: str | None = None) -> None:
|
|
|
|
|
|
sys.stderr.write(f"✗ {message}\n")
|
|
|
|
|
|
if suggestion:
|
|
|
|
|
|
sys.stderr.write(f" 建议: {suggestion}\n")
|