✨ feat: 对decide的功能进行调整
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "aide-plugin",
|
"name": "aide-plugin",
|
||||||
"description": "Aide 工作流体系插件,提供任务准备和执行的标准化流程",
|
"description": "Aide 工作流体系插件,提供任务准备和执行的标准化流程",
|
||||||
"version": "1.0.0",
|
"version": "1.0.2",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "Aide Team"
|
"name": "Aide Team"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -14,6 +14,14 @@ argument-hint: [任务细则文档路径]
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## 前置准备
|
||||||
|
|
||||||
|
**首先触发 `aide` skill 学习 aide 命令的使用方法。**
|
||||||
|
|
||||||
|
这是必要步骤,确保你了解 `aide flow` 等命令的正确用法。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## 开始
|
## 开始
|
||||||
|
|
||||||
### 确定任务细则
|
### 确定任务细则
|
||||||
|
|||||||
@@ -7,6 +7,22 @@ argument-hint: [无参数]
|
|||||||
|
|
||||||
你正在使用 Aide 工作流体系。本命令帮助你快速认知项目并确保环境就绪。
|
你正在使用 Aide 工作流体系。本命令帮助你快速认知项目并确保环境就绪。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 核心约束(必须遵守)
|
||||||
|
|
||||||
|
**禁止直接读取或编辑 `.aide/` 目录下的任何文件!**
|
||||||
|
|
||||||
|
- ❌ 禁止使用 Read 工具读取 `.aide/config.toml`
|
||||||
|
- ❌ 禁止使用 Edit/Write 工具修改 `.aide/` 下的文件
|
||||||
|
- ✅ 必须通过 `aide config get/set` 读写配置
|
||||||
|
- ✅ 必须通过 `aide env set` 修改环境配置
|
||||||
|
- ✅ 配置有误时,触发 `env-config` skill 学习正确的设置方法
|
||||||
|
|
||||||
|
**原因**:`.aide/` 是 aide 程序的内部数据目录,直接操作可能导致格式错误或状态不一致。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## 执行步骤
|
## 执行步骤
|
||||||
|
|
||||||
### 1. Aide 运行时环境检测
|
### 1. Aide 运行时环境检测
|
||||||
@@ -31,7 +47,7 @@ aide init
|
|||||||
|
|
||||||
此命令会:
|
此命令会:
|
||||||
- 创建 `.aide/` 目录(如不存在)
|
- 创建 `.aide/` 目录(如不存在)
|
||||||
- 生成默认配置文件 `.aide/config.toml`
|
- 生成默认配置文件
|
||||||
- 检查并更新 `.gitignore`
|
- 检查并更新 `.gitignore`
|
||||||
|
|
||||||
### 3. 项目认知
|
### 3. 项目认知
|
||||||
@@ -51,40 +67,40 @@ aide init
|
|||||||
aide env ensure
|
aide env ensure
|
||||||
```
|
```
|
||||||
|
|
||||||
此命令会:
|
|
||||||
- 读取 `.aide/config.toml` 中的环境配置
|
|
||||||
- 检测并修复项目开发环境
|
|
||||||
- 输出环境状态和配置信息
|
|
||||||
|
|
||||||
根据输出处理:
|
根据输出处理:
|
||||||
- `✓`:环境就绪,继续到步骤 5
|
- `✓`:环境就绪,**直接进入步骤 5**(不要再查看配置文件)
|
||||||
- `⚠`:有警告但可继续,记录并继续
|
- `⚠`:有警告但可继续,记录警告信息并继续
|
||||||
- `✗`:**触发 `env-config` skill**,按指导完成配置后重试
|
- `✗`:**触发 `env-config` skill**,学习如何使用 `aide env set` 命令修复配置,然后重试
|
||||||
|
|
||||||
**注意**:
|
**环境检测失败时的正确做法**:
|
||||||
- 此时不带 `--runtime` 参数,会读取项目配置文件检查项目环境
|
1. 触发 `env-config` skill 获取配置指导
|
||||||
- aide env 会输出项目配置信息,包括默认的任务文档路径,记住这些信息供后续使用
|
2. 使用 `aide env set <模块>.<配置项> <值>` 修改配置
|
||||||
|
3. 重新执行 `aide env ensure` 验证
|
||||||
|
|
||||||
|
**错误做法**(禁止):
|
||||||
|
- 直接编辑 `.aide/config.toml` 文件
|
||||||
|
|
||||||
### 5. 汇报就绪状态
|
### 5. 汇报就绪状态
|
||||||
|
|
||||||
向用户汇报:
|
环境检测通过后,直接向用户汇报(不要再读取配置文件):
|
||||||
|
|
||||||
```
|
```
|
||||||
项目概况:[来自步骤3的概要]
|
项目概况:[来自步骤3的概要]
|
||||||
|
|
||||||
环境状态:✓ 环境就绪 (python:3.12, ...)
|
环境状态:[aide env ensure 的输出结果]
|
||||||
|
|
||||||
项目配置:
|
|
||||||
- 任务原文档:task-now.md
|
|
||||||
- 任务细则:task-spec.md
|
|
||||||
|
|
||||||
Aide 已就绪,可用命令:
|
Aide 已就绪,可用命令:
|
||||||
- /aide:prep [文档路径] - 任务准备
|
- /aide:prep [文档路径] - 任务准备
|
||||||
- /aide:exec [文档路径] - 任务执行
|
- /aide:exec [文档路径] - 任务执行
|
||||||
```
|
```
|
||||||
|
|
||||||
|
> 注:任务文档路径等配置信息已在 aide env ensure 输出中显示,无需额外查看配置文件。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## 注意事项
|
## 注意事项
|
||||||
|
|
||||||
1. 如果在初始化过程中发现严重环境问题无法解决,建议用户修复后重开对话
|
1. **不要读取 `.aide/` 目录下的文件**——所有配置操作通过 aide 命令完成
|
||||||
2. 本命令只做认知和环境准备,不修改任何业务代码
|
2. 如果在初始化过程中发现严重环境问题无法解决,建议用户修复后重开对话
|
||||||
3. 所有输出使用简体中文
|
3. 本命令只做认知和环境准备,不修改任何业务代码
|
||||||
|
4. 所有输出使用简体中文
|
||||||
|
|||||||
@@ -14,6 +14,14 @@ argument-hint: [任务原文档路径]
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## 前置准备
|
||||||
|
|
||||||
|
**首先触发 `aide` skill 学习 aide 命令的使用方法。**
|
||||||
|
|
||||||
|
这是必要步骤,确保你了解 `aide flow` 等命令的正确用法。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## 开始
|
## 开始
|
||||||
|
|
||||||
### 启动流程追踪
|
### 启动流程追踪
|
||||||
@@ -95,15 +103,15 @@ aide flow next-step "任务优化完成,生成待定项"
|
|||||||
|
|
||||||
### 有待定项时
|
### 有待定项时
|
||||||
|
|
||||||
提交待定项数据:
|
1. 将待定项数据写入 JSON 文件(如 `.aide/pending-items.json`)
|
||||||
|
2. 提交待定项数据:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
aide decide submit '<json数据>'
|
aide decide submit .aide/pending-items.json
|
||||||
```
|
```
|
||||||
|
|
||||||
告知用户访问链接进行确认。
|
3. 告知用户访问输出的链接进行确认
|
||||||
|
4. 用户完成后获取结果:
|
||||||
用户完成后获取结果:
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
aide decide result
|
aide decide result
|
||||||
|
|||||||
@@ -208,25 +208,31 @@ aide flow 会自动校验环节跳转是否合理:
|
|||||||
|
|
||||||
## aide decide - 待定项确认
|
## aide decide - 待定项确认
|
||||||
|
|
||||||
通过 Web 界面处理待定项确认。
|
通过 Web 界面处理待定项确认。服务在后台运行,用户完成决策后自动关闭。
|
||||||
|
|
||||||
```
|
```
|
||||||
aide decide {submit,result} ...
|
aide decide {submit,result} ...
|
||||||
|
|
||||||
子命令:
|
子命令:
|
||||||
submit <json> 提交待定项数据并启动 Web 服务
|
submit <file> 从文件读取待定项数据,启动后台 Web 服务
|
||||||
result 获取用户决策结果
|
result 获取用户决策结果
|
||||||
```
|
```
|
||||||
|
|
||||||
### aide decide submit
|
### aide decide submit
|
||||||
|
|
||||||
提交待定项数据并启动 Web 服务。
|
从 JSON 文件读取待定项数据,启动后台 Web 服务,立即返回。
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
aide decide submit '<json数据>'
|
aide decide submit <json文件路径>
|
||||||
```
|
```
|
||||||
|
|
||||||
**JSON 格式**:
|
**使用流程**:
|
||||||
|
1. 将待定项数据写入 JSON 文件
|
||||||
|
2. 执行 `aide decide submit <文件路径>` 启动服务
|
||||||
|
3. 告知用户访问 Web 界面进行决策
|
||||||
|
4. 用户完成后执行 `aide decide result` 获取结果
|
||||||
|
|
||||||
|
**JSON 文件格式**:
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"task": "任务简述",
|
"task": "任务简述",
|
||||||
@@ -267,7 +273,7 @@ aide decide submit '<json数据>'
|
|||||||
|
|
||||||
| 配置项 | 默认值 | 说明 |
|
| 配置项 | 默认值 | 说明 |
|
||||||
|--------|--------|------|
|
|--------|--------|------|
|
||||||
| `port` | 3721 | 起始端口 |
|
| `port` | 3721 | 起始端口(自动探测可用端口) |
|
||||||
| `bind` | `"127.0.0.1"` | 监听地址,设为 `"0.0.0.0"` 可允许外部访问 |
|
| `bind` | `"127.0.0.1"` | 监听地址,设为 `"0.0.0.0"` 可允许外部访问 |
|
||||||
| `url` | `""` | 自定义访问地址,为空时自动生成 |
|
| `url` | `""` | 自定义访问地址,为空时自动生成 |
|
||||||
| `timeout` | 0 | 超时时间(秒),0 表示不超时 |
|
| `timeout` | 0 | 超时时间(秒),0 表示不超时 |
|
||||||
@@ -276,10 +282,11 @@ aide decide submit '<json数据>'
|
|||||||
```
|
```
|
||||||
→ Web 服务已启动
|
→ Web 服务已启动
|
||||||
→ 请访问: http://localhost:3721
|
→ 请访问: http://localhost:3721
|
||||||
→ 等待用户完成决策...
|
→ 用户完成决策后执行 aide decide result 获取结果
|
||||||
✓ 决策已完成
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
> 注:服务在后台运行,命令立即返回。用户提交决策后服务自动关闭。
|
||||||
|
|
||||||
### aide decide result
|
### aide decide result
|
||||||
|
|
||||||
获取用户决策结果。
|
获取用户决策结果。
|
||||||
@@ -298,6 +305,10 @@ aide decide result
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**错误情况**:
|
||||||
|
- 尚无决策结果(服务运行中):提示等待用户完成操作
|
||||||
|
- 尚无决策结果(服务已关闭):提示重新执行 submit
|
||||||
|
|
||||||
> 注:`note` 字段仅在用户添加备注时出现
|
> 注:`note` 字段仅在用户添加备注时出现
|
||||||
> 注:如果数据中有 `recommend` 字段,对应选项会默认选中
|
> 注:如果数据中有 `recommend` 字段,对应选项会默认选中
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
"""aide decide 模块入口。"""
|
"""aide decide 模块入口。"""
|
||||||
|
|
||||||
from .cli import cmd_decide, cmd_decide_result, cmd_decide_submit
|
from .cli import cmd_decide_result, cmd_decide_submit
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
"cmd_decide",
|
|
||||||
"cmd_decide_submit",
|
"cmd_decide_submit",
|
||||||
"cmd_decide_result",
|
"cmd_decide_result",
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -3,57 +3,90 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import json
|
import json
|
||||||
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
|
import time
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
|
from aide.core import output
|
||||||
from aide.decide.errors import DecideError
|
from aide.decide.errors import DecideError
|
||||||
from aide.decide.server import DecideServer
|
|
||||||
from aide.decide.storage import DecideStorage
|
from aide.decide.storage import DecideStorage
|
||||||
from aide.decide.types import DecideInput
|
from aide.decide.types import DecideInput
|
||||||
|
|
||||||
|
|
||||||
def cmd_decide(args) -> bool:
|
def cmd_decide_submit(file_path: str) -> bool:
|
||||||
"""aide decide 统一入口。"""
|
"""从文件读取数据,启动后台 Web 服务。"""
|
||||||
if getattr(args, "data", None) == "result":
|
|
||||||
return cmd_decide_result()
|
|
||||||
if getattr(args, "data", None) is None:
|
|
||||||
_print_error("缺少参数: 需要传入 JSON 数据或 result")
|
|
||||||
return False
|
|
||||||
return cmd_decide_submit(args.data)
|
|
||||||
|
|
||||||
|
|
||||||
def cmd_decide_submit(json_data: str) -> bool:
|
|
||||||
"""提交待定项并启动 Web 服务。"""
|
|
||||||
root = Path.cwd()
|
root = Path.cwd()
|
||||||
storage = DecideStorage(root)
|
storage = DecideStorage(root)
|
||||||
|
|
||||||
|
# 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
|
||||||
|
|
||||||
try:
|
try:
|
||||||
raw = json.loads(json_data)
|
raw = json.loads(json_file.read_text(encoding="utf-8"))
|
||||||
except json.JSONDecodeError as exc:
|
except json.JSONDecodeError as exc:
|
||||||
_print_error(f"JSON 解析失败: {exc}", "检查 JSON 格式是否正确")
|
_print_error(f"JSON 解析失败: {exc}", "检查 JSON 格式是否正确")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
# 2. 验证数据格式
|
||||||
try:
|
try:
|
||||||
decide_input = DecideInput.from_dict(raw)
|
decide_input = DecideInput.from_dict(raw)
|
||||||
except DecideError as exc:
|
except DecideError as exc:
|
||||||
_print_error(f"数据验证失败: {exc}", "检查必填字段是否完整")
|
_print_error(f"数据验证失败: {exc}", "检查必填字段是否完整")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
# 3. 保存到 pending.json
|
||||||
try:
|
try:
|
||||||
storage.save_pending(decide_input)
|
storage.save_pending(decide_input)
|
||||||
except DecideError as exc:
|
except DecideError as exc:
|
||||||
_print_error(str(exc))
|
_print_error(str(exc))
|
||||||
return False
|
return False
|
||||||
|
|
||||||
server = DecideServer(root, storage)
|
# 4. 启动后台服务
|
||||||
return server.start()
|
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
|
||||||
|
|
||||||
|
|
||||||
def cmd_decide_result() -> bool:
|
def cmd_decide_result() -> bool:
|
||||||
"""读取最新决策结果并输出 JSON。"""
|
"""获取决策结果(服务在用户提交后自动关闭)。"""
|
||||||
root = Path.cwd()
|
root = Path.cwd()
|
||||||
storage = DecideStorage(root)
|
storage = DecideStorage(root)
|
||||||
|
|
||||||
|
# 检查 pending
|
||||||
try:
|
try:
|
||||||
pending = storage.load_pending()
|
pending = storage.load_pending()
|
||||||
except DecideError as exc:
|
except DecideError as exc:
|
||||||
@@ -61,14 +94,15 @@ def cmd_decide_result() -> bool:
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
if pending is None:
|
if pending is None:
|
||||||
_print_error("未找到待定项数据", "请先执行 aide decide submit '<json>'")
|
_print_error("未找到待定项数据", "请先执行 aide decide submit <file>")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
session_id = pending.meta.session_id if pending.meta else None
|
session_id = pending.meta.session_id if pending.meta else None
|
||||||
if not session_id:
|
if not session_id:
|
||||||
_print_error("决策结果已过期", "pending.json 已被更新,请重新执行 aide decide submit '<json>'")
|
_print_error("数据异常", "pending.json 缺少 session_id,请重新执行 aide decide submit")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
# 检查结果
|
||||||
try:
|
try:
|
||||||
result = storage.load_result()
|
result = storage.load_result()
|
||||||
except DecideError as exc:
|
except DecideError as exc:
|
||||||
@@ -76,20 +110,20 @@ def cmd_decide_result() -> bool:
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
if result is None:
|
if result is None:
|
||||||
has_history = any(
|
# 检查服务是否还在运行
|
||||||
path.is_file()
|
if storage.is_server_running():
|
||||||
and path.name.endswith(".json")
|
|
||||||
and path.name != "pending.json"
|
|
||||||
for path in storage.decisions_dir.glob("*.json")
|
|
||||||
)
|
|
||||||
if has_history:
|
|
||||||
_print_error("决策结果已过期", "pending.json 已被更新,请重新执行 aide decide submit '<json>'")
|
|
||||||
else:
|
|
||||||
_print_error("尚无决策结果", "请等待用户在 Web 界面完成操作")
|
_print_error("尚无决策结果", "请等待用户在 Web 界面完成操作")
|
||||||
|
else:
|
||||||
|
# 服务已关闭但没有结果,可能是超时或异常
|
||||||
|
_print_error("尚无决策结果", "服务可能已超时关闭,请重新执行 aide decide submit")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
# 输出结果
|
||||||
payload = json.dumps(result.to_dict(), ensure_ascii=False, separators=(",", ":"))
|
payload = json.dumps(result.to_dict(), ensure_ascii=False, separators=(",", ":"))
|
||||||
print(payload)
|
print(payload)
|
||||||
|
|
||||||
|
# 清理服务状态文件(如存在)
|
||||||
|
storage.clear_server_info()
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
47
aide-program/aide/decide/daemon.py
Normal file
47
aide-program/aide/decide/daemon.py
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
"""后台服务入口点。
|
||||||
|
|
||||||
|
通过 subprocess 启动,独立运行 HTTP 服务。
|
||||||
|
用法: python -m aide.decide.daemon <project_root>
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import os
|
||||||
|
import signal
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> int:
|
||||||
|
if len(sys.argv) < 2:
|
||||||
|
sys.stderr.write("用法: python -m aide.decide.daemon <project_root>\n")
|
||||||
|
return 1
|
||||||
|
|
||||||
|
root = Path(sys.argv[1])
|
||||||
|
if not root.exists():
|
||||||
|
sys.stderr.write(f"目录不存在: {root}\n")
|
||||||
|
return 1
|
||||||
|
|
||||||
|
# 延迟导入,避免循环依赖
|
||||||
|
from aide.decide.server import DecideServer
|
||||||
|
from aide.decide.storage import DecideStorage
|
||||||
|
|
||||||
|
storage = DecideStorage(root)
|
||||||
|
server = DecideServer(root, storage)
|
||||||
|
|
||||||
|
# 注册信号处理
|
||||||
|
def handle_sigterm(signum: int, frame: object) -> None:
|
||||||
|
server.stop("terminated")
|
||||||
|
|
||||||
|
signal.signal(signal.SIGTERM, handle_sigterm)
|
||||||
|
|
||||||
|
# 获取当前进程 PID
|
||||||
|
pid = os.getpid()
|
||||||
|
|
||||||
|
# 启动服务(会保存 server.json,阻塞等待)
|
||||||
|
success = server.start_daemon(pid)
|
||||||
|
return 0 if success else 1
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
sys.exit(main())
|
||||||
@@ -95,6 +95,51 @@ class DecideServer:
|
|||||||
self.should_close = True
|
self.should_close = True
|
||||||
self.close_reason = reason
|
self.close_reason = reason
|
||||||
|
|
||||||
|
def start_daemon(self, pid: int) -> bool:
|
||||||
|
"""作为后台进程启动服务(由 daemon.py 调用)。
|
||||||
|
|
||||||
|
与 start() 的区别:
|
||||||
|
- 不输出到 stdout(后台运行)
|
||||||
|
- 保存服务信息到 server.json
|
||||||
|
- 退出时清理 server.json
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
config = ConfigManager(self.root).load_config()
|
||||||
|
start_port = _get_int(config, "decide", "port", default=3721)
|
||||||
|
self.timeout = _get_int(config, "decide", "timeout", default=0)
|
||||||
|
self.bind = _get_str(config, "decide", "bind", default="127.0.0.1")
|
||||||
|
self.url = _get_str(config, "decide", "url", default="")
|
||||||
|
|
||||||
|
available = self._find_available_port(start_port)
|
||||||
|
if available is None:
|
||||||
|
return False
|
||||||
|
self.port = available
|
||||||
|
|
||||||
|
handlers = DecideHandlers(
|
||||||
|
storage=self.storage,
|
||||||
|
web_dir=self.web_dir,
|
||||||
|
stop_callback=self.stop,
|
||||||
|
)
|
||||||
|
RequestHandler = self._build_request_handler(handlers)
|
||||||
|
self.httpd = DecideHTTPServer((self.bind, self.port), RequestHandler, handlers)
|
||||||
|
self.httpd.timeout = 1.0
|
||||||
|
|
||||||
|
# 生成访问地址
|
||||||
|
access_url = self.url if self.url else f"http://localhost:{self.port}"
|
||||||
|
|
||||||
|
# 保存服务信息(供 CLI 读取)
|
||||||
|
self.storage.save_server_info(pid, self.port, access_url)
|
||||||
|
|
||||||
|
# 阻塞等待用户操作
|
||||||
|
self._serve_forever()
|
||||||
|
|
||||||
|
# 清理服务信息
|
||||||
|
self.storage.clear_server_info()
|
||||||
|
return True
|
||||||
|
except Exception:
|
||||||
|
self.storage.clear_server_info()
|
||||||
|
return False
|
||||||
|
|
||||||
def _find_available_port(self, start: int) -> int | None:
|
def _find_available_port(self, start: int) -> int | None:
|
||||||
attempts = 10
|
attempts = 10
|
||||||
for offset in range(attempts):
|
for offset in range(attempts):
|
||||||
|
|||||||
@@ -115,3 +115,49 @@ class DecideStorage:
|
|||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
raise DecideError(f"无法解析 {path.name}: {exc}")
|
raise DecideError(f"无法解析 {path.name}: {exc}")
|
||||||
|
|
||||||
|
# ========== 服务状态管理 ==========
|
||||||
|
|
||||||
|
@property
|
||||||
|
def server_info_path(self) -> Path:
|
||||||
|
"""服务状态文件路径。"""
|
||||||
|
return self.decisions_dir / "server.json"
|
||||||
|
|
||||||
|
def save_server_info(self, pid: int, port: int, url: str) -> None:
|
||||||
|
"""保存后台服务信息。"""
|
||||||
|
self.ensure_ready()
|
||||||
|
info = {
|
||||||
|
"pid": pid,
|
||||||
|
"port": port,
|
||||||
|
"url": url,
|
||||||
|
"started_at": datetime.now().astimezone().isoformat(timespec="seconds"),
|
||||||
|
}
|
||||||
|
self._save_atomic(self.server_info_path, info)
|
||||||
|
|
||||||
|
def load_server_info(self) -> dict[str, Any] | None:
|
||||||
|
"""读取服务信息,不存在返回 None。"""
|
||||||
|
if not self.server_info_path.exists():
|
||||||
|
return None
|
||||||
|
try:
|
||||||
|
return self._load_json(self.server_info_path)
|
||||||
|
except DecideError:
|
||||||
|
return None
|
||||||
|
|
||||||
|
def clear_server_info(self) -> None:
|
||||||
|
"""清理服务状态文件。"""
|
||||||
|
if self.server_info_path.exists():
|
||||||
|
try:
|
||||||
|
self.server_info_path.unlink()
|
||||||
|
except OSError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def is_server_running(self) -> bool:
|
||||||
|
"""检查后台服务是否运行中。"""
|
||||||
|
info = self.load_server_info()
|
||||||
|
if not info or "pid" not in info:
|
||||||
|
return False
|
||||||
|
try:
|
||||||
|
os.kill(info["pid"], 0) # 检查进程是否存在
|
||||||
|
return True
|
||||||
|
except (ProcessLookupError, PermissionError, OSError):
|
||||||
|
return False
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ from typing import Any
|
|||||||
|
|
||||||
from aide.core import output
|
from aide.core import output
|
||||||
from aide.core.config import ConfigManager
|
from aide.core.config import ConfigManager
|
||||||
from aide.decide import cmd_decide
|
|
||||||
from aide.env.manager import EnvManager
|
from aide.env.manager import EnvManager
|
||||||
from aide.flow.tracker import FlowTracker
|
from aide.flow.tracker import FlowTracker
|
||||||
|
|
||||||
@@ -134,9 +133,9 @@ def build_parser() -> argparse.ArgumentParser:
|
|||||||
decide_parser = subparsers.add_parser("decide", help="待定项确认与决策记录")
|
decide_parser = subparsers.add_parser("decide", help="待定项确认与决策记录")
|
||||||
decide_subparsers = decide_parser.add_subparsers(dest="decide_cmd")
|
decide_subparsers = decide_parser.add_subparsers(dest="decide_cmd")
|
||||||
|
|
||||||
# aide decide submit '<json>'
|
# aide decide submit <file>
|
||||||
decide_submit_parser = decide_subparsers.add_parser("submit", help="提交待定项数据并启动 Web 服务")
|
decide_submit_parser = decide_subparsers.add_parser("submit", help="提交待定项数据并启动 Web 服务")
|
||||||
decide_submit_parser.add_argument("data", help="待定项 JSON 数据")
|
decide_submit_parser.add_argument("file", help="待定项 JSON 数据文件路径")
|
||||||
decide_submit_parser.set_defaults(func=handle_decide_submit)
|
decide_submit_parser.set_defaults(func=handle_decide_submit)
|
||||||
|
|
||||||
# aide decide result
|
# aide decide result
|
||||||
@@ -302,18 +301,18 @@ def handle_decide_help(args: argparse.Namespace) -> bool:
|
|||||||
print("usage: aide decide {submit,result} ...")
|
print("usage: aide decide {submit,result} ...")
|
||||||
print("")
|
print("")
|
||||||
print("子命令:")
|
print("子命令:")
|
||||||
print(" submit <json> 提交待定项数据并启动 Web 服务")
|
print(" submit <file> 从文件读取待定项数据,启动后台 Web 服务")
|
||||||
print(" result 获取用户决策结果")
|
print(" result 获取用户决策结果")
|
||||||
print("")
|
print("")
|
||||||
print("示例:")
|
print("示例:")
|
||||||
print(" aide decide submit '{\"task\":\"...\",\"source\":\"...\",\"items\":[...]}'")
|
print(" aide decide submit ./pending-items.json")
|
||||||
print(" aide decide result")
|
print(" aide decide result")
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def handle_decide_submit(args: argparse.Namespace) -> bool:
|
def handle_decide_submit(args: argparse.Namespace) -> bool:
|
||||||
from aide.decide import cmd_decide_submit
|
from aide.decide import cmd_decide_submit
|
||||||
return cmd_decide_submit(args.data)
|
return cmd_decide_submit(args.file)
|
||||||
|
|
||||||
|
|
||||||
def handle_decide_result(args: argparse.Namespace) -> bool:
|
def handle_decide_result(args: argparse.Namespace) -> bool:
|
||||||
|
|||||||
Reference in New Issue
Block a user