From 39bddb92ee452b30e72e47bb02ad172bab237a8d Mon Sep 17 00:00:00 2001 From: cfdaily Date: Sun, 17 May 2026 00:30:50 +0800 Subject: [PATCH] auto-sync: 2026-05-17 00:30:50 --- src/main.py | 126 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 126 insertions(+) create mode 100644 src/main.py diff --git a/src/main.py b/src/main.py new file mode 100644 index 0000000..7f05095 --- /dev/null +++ b/src/main.py @@ -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")