重构 API 路由并新增工作流编排功能
后端: - 重构 agents, heartbeats, locks, meetings, resources, roles, workflows 路由 - 新增 orchestrator 和 providers 路由 - 新增 CLI 调用器和流程编排服务 - 添加日志配置和依赖项 前端: - 更新 AgentsPage、SettingsPage、WorkflowPage 页面 - 扩展 api.ts 新增 API 接口 其他: - 清理测试 agent 数据文件 - 新增示例工作流和项目审计报告 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,89 +1,82 @@
|
||||
"""
|
||||
文件锁 API 路由
|
||||
接入 FileLockService 服务,管理文件的排他锁
|
||||
"""
|
||||
from fastapi import APIRouter
|
||||
from pydantic import BaseModel
|
||||
from typing import List, Optional
|
||||
import time
|
||||
from typing import Optional
|
||||
from dataclasses import asdict
|
||||
|
||||
from ..services.file_lock import get_file_lock_service
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
locks_db = [
|
||||
{
|
||||
"file_path": "src/main.py",
|
||||
"agent_id": "claude-001",
|
||||
"agent_name": "Claude Code",
|
||||
"locked_at": time.time() - 3600
|
||||
},
|
||||
{
|
||||
"file_path": "src/utils.py",
|
||||
"agent_id": "kimi-001",
|
||||
"agent_name": "Kimi CLI",
|
||||
"locked_at": time.time() - 1800
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
class FileLock(BaseModel):
|
||||
class LockAcquireRequest(BaseModel):
|
||||
file_path: str
|
||||
agent_id: str
|
||||
agent_name: str = ""
|
||||
locked_at: float
|
||||
agent_name: Optional[str] = ""
|
||||
|
||||
|
||||
def format_elapsed(locked_at: float) -> str:
|
||||
"""格式化已锁定时间"""
|
||||
elapsed = time.time() - locked_at
|
||||
if elapsed < 60:
|
||||
return f"{int(elapsed)}秒"
|
||||
elif elapsed < 3600:
|
||||
return f"{int(elapsed / 60)}分钟"
|
||||
else:
|
||||
return f"{elapsed / 3600:.1f}小时"
|
||||
class LockReleaseRequest(BaseModel):
|
||||
file_path: str
|
||||
agent_id: str
|
||||
|
||||
|
||||
@router.get("")
|
||||
@router.get("/")
|
||||
async def list_locks():
|
||||
"""获取所有文件锁列表"""
|
||||
locks_with_display = []
|
||||
for lock in locks_db:
|
||||
lock_copy = lock.copy()
|
||||
lock_copy["elapsed_display"] = format_elapsed(lock["locked_at"])
|
||||
locks_with_display.append(lock_copy)
|
||||
return {"locks": locks_with_display}
|
||||
service = get_file_lock_service()
|
||||
locks = await service.get_locks()
|
||||
return {
|
||||
"locks": [
|
||||
{
|
||||
"file_path": lock.file_path,
|
||||
"agent_id": lock.agent_id,
|
||||
"agent_name": lock.agent_name,
|
||||
"acquired_at": lock.acquired_at,
|
||||
"elapsed_display": lock.elapsed_display
|
||||
}
|
||||
for lock in locks
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
@router.post("/acquire")
|
||||
async def acquire_lock(lock: FileLock):
|
||||
async def acquire_lock(request: LockAcquireRequest):
|
||||
"""获取文件锁"""
|
||||
# 检查是否已被锁定
|
||||
for existing in locks_db:
|
||||
if existing["file_path"] == lock.file_path:
|
||||
return {"success": False, "message": "File already locked"}
|
||||
|
||||
locks_db.append({
|
||||
"file_path": lock.file_path,
|
||||
"agent_id": lock.agent_id,
|
||||
"agent_name": lock.agent_name or lock.agent_id,
|
||||
"locked_at": time.time()
|
||||
})
|
||||
return {"success": True, "message": "Lock acquired"}
|
||||
service = get_file_lock_service()
|
||||
success = await service.acquire_lock(
|
||||
file_path=request.file_path,
|
||||
agent_id=request.agent_id,
|
||||
agent_name=request.agent_name or ""
|
||||
)
|
||||
if success:
|
||||
return {"success": True, "message": "Lock acquired"}
|
||||
return {"success": False, "message": "File already locked by another agent"}
|
||||
|
||||
|
||||
@router.post("/release")
|
||||
async def release_lock(data: dict):
|
||||
async def release_lock(request: LockReleaseRequest):
|
||||
"""释放文件锁"""
|
||||
file_path = data.get("file_path", "")
|
||||
agent_id = data.get("agent_id", "")
|
||||
global locks_db
|
||||
locks_db = [l for l in locks_db if not (l["file_path"] == file_path and l["agent_id"] == agent_id)]
|
||||
return {"success": True, "message": "Lock released"}
|
||||
service = get_file_lock_service()
|
||||
success = await service.release_lock(
|
||||
file_path=request.file_path,
|
||||
agent_id=request.agent_id
|
||||
)
|
||||
if success:
|
||||
return {"success": True, "message": "Lock released"}
|
||||
return {"success": False, "message": "Lock not found or not owned by this agent"}
|
||||
|
||||
|
||||
@router.get("/check")
|
||||
async def check_lock(file_path: str):
|
||||
"""检查文件锁定状态"""
|
||||
for lock in locks_db:
|
||||
if lock["file_path"] == file_path:
|
||||
return {"file_path": file_path, "locked": True, "locked_by": lock["agent_id"]}
|
||||
return {"file_path": file_path, "locked": False}
|
||||
service = get_file_lock_service()
|
||||
locked_by = await service.check_locked(file_path)
|
||||
return {
|
||||
"file_path": file_path,
|
||||
"locked": locked_by is not None,
|
||||
"locked_by": locked_by
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user