auto-sync: 2026-05-17 00:30:50
This commit is contained in:
+126
@@ -0,0 +1,126 @@
|
|||||||
|
"""v2.6 主入口 - FastAPI + Daemon ticker 共享 asyncio event loop"""
|
||||||
|
|
||||||
|
import asyncio
|
||||||
|
import logging
|
||||||
|
from contextlib import asynccontextmanager
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
import yaml
|
||||||
|
from fastapi import FastAPI
|
||||||
|
from fastapi.middleware.cors import CORSMiddleware
|
||||||
|
from fastapi.staticfiles import StaticFiles
|
||||||
|
|
||||||
|
logger = logging.getLogger("moziplus-v2")
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# 配置加载
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
DEFAULT_CONFIG_PATH = Path(__file__).parent.parent / "config" / "default.yaml"
|
||||||
|
|
||||||
|
|
||||||
|
def load_config() -> dict:
|
||||||
|
"""加载全局默认配置"""
|
||||||
|
if DEFAULT_CONFIG_PATH.exists():
|
||||||
|
with open(DEFAULT_CONFIG_PATH) as f:
|
||||||
|
return yaml.safe_load(f) or {}
|
||||||
|
return {}
|
||||||
|
|
||||||
|
|
||||||
|
config = load_config()
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Daemon ticker(占位,F6 实现)
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
_ticker_task: asyncio.Task | None = None
|
||||||
|
|
||||||
|
|
||||||
|
async def _run_ticker():
|
||||||
|
"""Daemon ticker 主循环(占位)"""
|
||||||
|
tick_interval = config.get("daemon", {}).get("tick_interval", 30)
|
||||||
|
logger.info("Ticker started (interval=%ss)", tick_interval)
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
# F6 会在这里注入真正的 tick 逻辑
|
||||||
|
await asyncio.sleep(tick_interval)
|
||||||
|
except asyncio.CancelledError:
|
||||||
|
logger.info("Ticker cancelled")
|
||||||
|
return
|
||||||
|
except Exception:
|
||||||
|
logger.exception("Tick error")
|
||||||
|
await asyncio.sleep(tick_interval)
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# FastAPI 生命周期
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
@asynccontextmanager
|
||||||
|
async def lifespan(app: FastAPI):
|
||||||
|
"""启动 Daemon ticker,关闭时清理"""
|
||||||
|
global _ticker_task
|
||||||
|
logger.info("moziplus-v2 starting...")
|
||||||
|
_ticker_task = asyncio.create_task(_run_ticker())
|
||||||
|
yield
|
||||||
|
if _ticker_task:
|
||||||
|
_ticker_task.cancel()
|
||||||
|
try:
|
||||||
|
await _ticker_task
|
||||||
|
except asyncio.CancelledError:
|
||||||
|
pass
|
||||||
|
logger.info("moziplus-v2 stopped")
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# FastAPI app
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
app = FastAPI(
|
||||||
|
title="Sanguo MoziPlus v2",
|
||||||
|
description="AI Native DevOps Platform - Blackboard Architecture",
|
||||||
|
version="2.6.0",
|
||||||
|
lifespan=lifespan,
|
||||||
|
)
|
||||||
|
|
||||||
|
app.add_middleware(
|
||||||
|
CORSMiddleware,
|
||||||
|
allow_origins=["*"],
|
||||||
|
allow_credentials=True,
|
||||||
|
allow_methods=["*"],
|
||||||
|
allow_headers=["*"],
|
||||||
|
)
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# 健康端点
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/api/daemon/status")
|
||||||
|
async def daemon_status():
|
||||||
|
"""Daemon 健康检查"""
|
||||||
|
return {
|
||||||
|
"status": "running",
|
||||||
|
"version": "2.6.0",
|
||||||
|
"ticker_running": _ticker_task is not None and not _ticker_task.done(),
|
||||||
|
"config": {
|
||||||
|
"tick_interval": config.get("daemon", {}).get("tick_interval", 30),
|
||||||
|
"max_global_agents": config.get("daemon", {}).get("max_global_agents", 5),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/api/projects")
|
||||||
|
async def list_projects():
|
||||||
|
"""列出所有项目(占位,F3 实现)"""
|
||||||
|
return {"projects": []}
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# 静态文件服务(前端 dist/,F18 实现)
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
DIST_DIR = Path(__file__).parent / "frontend" / "dist"
|
||||||
|
if DIST_DIR.exists():
|
||||||
|
app.mount("/", StaticFiles(directory=str(DIST_DIR), html=True), name="frontend")
|
||||||
Reference in New Issue
Block a user