diff --git a/.doc/agents/kimi-001/info.json b/.doc/agents/kimi-001/info.json new file mode 100644 index 0000000..dda10da --- /dev/null +++ b/.doc/agents/kimi-001/info.json @@ -0,0 +1,9 @@ +{ + "agent_id": "kimi-001", + "name": "Kimi CLI", + "role": "architect", + "model": "kimi-k2", + "description": "Kimi CLI - architect", + "created_at": "2026-03-09T18:23:33.409369", + "status": "idle" +} \ No newline at end of file diff --git a/.doc/agents/kimi-001/state.json b/.doc/agents/kimi-001/state.json new file mode 100644 index 0000000..76d8ba6 --- /dev/null +++ b/.doc/agents/kimi-001/state.json @@ -0,0 +1,7 @@ +{ + "agent_id": "kimi-001", + "current_task": "", + "progress": 0, + "working_files": [], + "last_update": "2026-03-09T18:23:33.413023" +} \ No newline at end of file diff --git a/.doc/agents/opencode-001/info.json b/.doc/agents/opencode-001/info.json new file mode 100644 index 0000000..379a47b --- /dev/null +++ b/.doc/agents/opencode-001/info.json @@ -0,0 +1,9 @@ +{ + "agent_id": "opencode-001", + "name": "OpenCode", + "role": "reviewer", + "model": "opencode-v1", + "description": "OpenCode - reviewer", + "created_at": "2026-03-09T18:23:34.314235", + "status": "idle" +} \ No newline at end of file diff --git a/.doc/agents/opencode-001/state.json b/.doc/agents/opencode-001/state.json new file mode 100644 index 0000000..f84e00f --- /dev/null +++ b/.doc/agents/opencode-001/state.json @@ -0,0 +1,7 @@ +{ + "agent_id": "opencode-001", + "current_task": "", + "progress": 0, + "working_files": [], + "last_update": "2026-03-09T18:23:34.317455" +} \ No newline at end of file diff --git a/.doc/cache/heartbeats.json b/.doc/cache/heartbeats.json index f846a41..7f63627 100644 --- a/.doc/cache/heartbeats.json +++ b/.doc/cache/heartbeats.json @@ -1,10 +1,10 @@ { "claude-001": { "agent_id": "claude-001", - "last_heartbeat": "2026-03-05T10:31:09.197892", + "last_heartbeat": "2026-03-09T18:23:29.886176", "status": "idle", "current_task": "", - "progress": 100 + "progress": 0 }, "kimi-002": { "agent_id": "kimi-002", @@ -47,5 +47,19 @@ "status": "waiting", "current_task": "等待会议: meeting-002", "progress": 0 + }, + "kimi-001": { + "agent_id": "kimi-001", + "last_heartbeat": "2026-03-09T18:23:33.415851", + "status": "idle", + "current_task": "", + "progress": 0 + }, + "opencode-001": { + "agent_id": "opencode-001", + "last_heartbeat": "2026-03-09T18:23:34.321019", + "status": "idle", + "current_task": "", + "progress": 0 } } \ No newline at end of file diff --git a/.doc/cache/meeting_queue.json b/.doc/cache/meeting_queue.json index 730477f..9c1b1d3 100644 --- a/.doc/cache/meeting_queue.json +++ b/.doc/cache/meeting_queue.json @@ -35,16 +35,15 @@ "meeting_id": "test-meeting-001", "title": "测试会议", "expected_attendees": [ - "agent-001", - "agent-002" + "claude-001", + "kimi-001" ], "arrived_attendees": [ - "agent-001", - "agent-002" + "claude-001" ], - "status": "ended", - "created_at": "2026-03-09T09:28:05.309297", - "started_at": "2026-03-09T09:28:05.357846", + "status": "waiting", + "created_at": "2026-03-09T18:05:28.657165", + "started_at": "", "min_required": 2 }, "meeting-001": { diff --git a/backend/app/main.py b/backend/app/main.py index 6840867..6e9087b 100644 --- a/backend/app/main.py +++ b/backend/app/main.py @@ -20,7 +20,15 @@ app = FastAPI( # 配置 CORS - 允许前端访问 app.add_middleware( CORSMiddleware, - allow_origins=["http://localhost:3000", "http://127.0.0.1:3000"], + allow_origins=[ # 必须具体列出,不能使用 * 当 allow_credentials=True + "http://localhost:3000", + "http://localhost:3001", + "http://127.0.0.1:3000", + "http://127.0.0.1:3001", + "http://localhost:5173", + "http://127.0.0.1:5173", + "http://localhost:8080", + ], allow_credentials=True, allow_methods=["*"], allow_headers=["*"], diff --git a/backend/app/routers/agents.py b/backend/app/routers/agents.py index 8c4a5d8..eddfd02 100644 --- a/backend/app/routers/agents.py +++ b/backend/app/routers/agents.py @@ -33,6 +33,7 @@ class AgentCreate(BaseModel): # Agent状态存储 agent_states_db = {} +@router.get("") @router.get("/") async def list_agents(): """获取所有 Agent 列表""" diff --git a/backend/app/routers/heartbeats.py b/backend/app/routers/heartbeats.py index a6f321f..4e75fa3 100644 --- a/backend/app/routers/heartbeats.py +++ b/backend/app/routers/heartbeats.py @@ -17,6 +17,7 @@ class Heartbeat(BaseModel): is_timeout: bool = False +@router.get("") @router.get("/") async def list_heartbeats(): """获取所有 Agent 心跳""" diff --git a/backend/app/routers/locks.py b/backend/app/routers/locks.py index 0e2319a..96ac465 100644 --- a/backend/app/routers/locks.py +++ b/backend/app/routers/locks.py @@ -41,6 +41,7 @@ def format_elapsed(locked_at: float) -> str: else: return f"{elapsed / 3600:.1f}小时" +@router.get("") @router.get("/") async def list_locks(): """获取所有文件锁列表""" diff --git a/backend/app/routers/meetings.py b/backend/app/routers/meetings.py index 191045d..fcb7108 100644 --- a/backend/app/routers/meetings.py +++ b/backend/app/routers/meetings.py @@ -29,6 +29,7 @@ class MeetingCreate(BaseModel): attendees: List[str] = [] +@router.get("") @router.get("/") async def list_meetings(): """获取所有会议列表""" diff --git a/backend/app/routers/resources.py b/backend/app/routers/resources.py index 92bd38f..a9a7700 100644 --- a/backend/app/routers/resources.py +++ b/backend/app/routers/resources.py @@ -33,22 +33,37 @@ async def execute_task(request: TaskRequest): @router.get("/status") async def get_all_status(): """获取所有 Agent 状态""" - return { - "agents": [ - { - "agent_id": "claude-001", - "status": "working", - "current_task": "开发功能", - "progress": 75 + from ..services.agent_registry import get_agent_registry + from ..services.heartbeat import get_heartbeat_service + + registry = get_agent_registry() + heartbeat_service = get_heartbeat_service() + + # 获取所有已注册的 Agent + all_agents = await registry.list_agents() + agent_map = {a.agent_id: a for a in all_agents} + + # 获取所有心跳 + heartbeats_data = await heartbeat_service.get_all_heartbeats() + + result = [] + for agent_id, agent in agent_map.items(): + heartbeat = heartbeats_data.get(agent_id) + result.append({ + "agent_id": agent_id, + "info": { + "name": agent.name, + "role": agent.role, + "model": agent.model }, - { - "agent_id": "kimi-001", - "status": "idle", - "current_task": "", - "progress": 0 + "heartbeat": { + "status": heartbeat.status if heartbeat else "offline", + "current_task": heartbeat.current_task if heartbeat else "", + "progress": heartbeat.progress if heartbeat else 0 } - ] - } + }) + + return {"agents": result} @router.post("/parse-task") diff --git a/backend/cli.py b/backend/cli.py index 74d715d..25aa9d5 100644 --- a/backend/cli.py +++ b/backend/cli.py @@ -1009,7 +1009,7 @@ def human_pending_tasks( for t in tasks: urgent_mark = "[red]⚠️[/red] " if t.is_urgent else "" console.print(f" {urgent_mark}[yellow]{t.id}[/yellow]") - console.print(f" From: {t.from} | Priority: {t.priority}") + console.print(f" From: {t.from_user} | Priority: {t.priority}") if t.title: console.print(f" Title: {t.title}") console.print(f" Content: {t.content}") @@ -1029,7 +1029,7 @@ def human_urgent_tasks(): if tasks: console.print(Panel(f"[red]⚠️ Urgent Tasks ({len(tasks)}):[/red]", border_style="red")) for t in tasks: - console.print(f" [yellow]{t.id}[/yellow] | {t.from}") + console.print(f" [yellow]{t.id}[/yellow] | {t.from_user}") console.print(f" Content: {t.content}") else: console.print(Panel("[green]No urgent tasks[/green]", border_style="green")) @@ -1076,7 +1076,7 @@ def human_pending_comments( if comments: console.print(Panel(f"[cyan]Pending Comments ({len(comments)}):[/cyan]", border_style="cyan")) for c in comments: - console.print(f" [yellow]{c.id}[/yellow] | {c.from} -> {c.meeting_id}") + console.print(f" [yellow]{c.id}[/yellow] | {c.from_user} -> {c.meeting_id}") console.print(f" Type: {c.type} | Priority: {c.priority}") console.print(f" Content: {c.content}") else: diff --git a/frontend/src/lib/api.ts b/frontend/src/lib/api.ts index 85db65d..d19639f 100644 --- a/frontend/src/lib/api.ts +++ b/frontend/src/lib/api.ts @@ -19,7 +19,19 @@ async function request( endpoint: string, options?: RequestInit ): Promise { - const url = `${API_BASE}${endpoint}`; + // 自动添加末尾斜杠避免 307 重定向 + let normalizedEndpoint = endpoint; + if (endpoint.includes('?')) { + // 带查询参数:在 ? 之前添加斜杠 + const [path, query] = endpoint.split('?'); + if (!path.endsWith('/')) { + normalizedEndpoint = `${path}/?${query}`; + } + } else if (!endpoint.match(/\/[^/]+$/)) { + // 不带查询参数且不以路径结尾:添加斜杠 + normalizedEndpoint = `${endpoint}/`; + } + const url = `${API_BASE}${normalizedEndpoint}`; const response = await fetch(url, { headers: { 'Content-Type': 'application/json', diff --git a/resources_errors.txt b/resources_errors.txt new file mode 100644 index 0000000..c0c40f8 --- /dev/null +++ b/resources_errors.txt @@ -0,0 +1,2 @@ +Total messages: 3 (Errors: 0, Warnings: 0) +Returning 0 messages for level "error" diff --git a/test_errors.txt b/test_errors.txt new file mode 100644 index 0000000..7f170e2 --- /dev/null +++ b/test_errors.txt @@ -0,0 +1,5 @@ +Total messages: 5 (Errors: 2, Warnings: 0) +Returning 2 messages for level "error" + +[ERROR] Failed to load resource: the server responded with a status of 404 (Not Found) @ http://localhost:8000/api/agents/control/list:0 +[ERROR] Failed to load resource: the server responded with a status of 404 (Not Found) @ http://localhost:8000/api/agents/control/list:0 \ No newline at end of file diff --git a/test_swarm_frontend.py b/test_swarm_frontend.py new file mode 100644 index 0000000..5b1f51b --- /dev/null +++ b/test_swarm_frontend.py @@ -0,0 +1,183 @@ +""" +Swarm 前端完整测试 +测试所有页面功能和 API +""" +from playwright.sync_api import sync_playwright +import json +import time + +BASE_URL = "http://localhost:3000" +API_BASE = "http://localhost:8000/api" + +def test_api_endpoints(): + """测试后端 API 端点""" + print("\n=== 测试后端 API ===") + import urllib.request + + endpoints = [ + "/health", + "/agents/", + "/locks/", + "/heartbeats/", + "/workflows/list", + "/workflows/files", + "/status", + "/humans/summary", + ] + + results = {} + for endpoint in endpoints: + try: + url = f"{API_BASE}{endpoint}" + with urllib.request.urlopen(url, timeout=5) as response: + data = response.read().decode('utf-8') + results[endpoint] = {"status": response.status, "data": data[:100]} + print(f" ✓ {endpoint}: {response.status}") + except Exception as e: + results[endpoint] = {"status": "error", "error": str(e)} + print(f" ✗ {endpoint}: {e}") + + # 测试 agents_control 端点 + control_endpoints = [ + "/api/agents/control/summary", + "/api/agents/control/list", + ] + + print("\n=== 测试 agents_control API ===") + for endpoint in control_endpoints: + try: + url = f"http://localhost:8000{endpoint}" + with urllib.request.urlopen(url, timeout=5) as response: + data = response.read().decode('utf-8') + results[endpoint] = {"status": response.status} + print(f" ✓ {endpoint}: {response.status}") + except Exception as e: + results[endpoint] = {"status": "error", "error": str(e)} + print(f" ✗ {endpoint}: {e}") + + return results + +def test_frontend_pages(): + """测试前端页面""" + print("\n=== 测试前端页面 ===") + + with sync_playwright() as p: + browser = p.chromium.launch(headless=False) + context = browser.new_context() + page = context.new_page() + + # 收集控制台错误 + console_errors = [] + def on_console(msg): + if msg.type == "error": + console_errors.append(msg.text) + + page.on("console", on_console) + + issues = {} + + try: + # 1. Dashboard 页面 + print("\n1. 测试 Dashboard 页面...") + page.goto(f"{BASE_URL}/") + page.wait_for_load_state("networkidle", timeout=10000) + page.screenshot(path="D:/ccProg/swarm/test_screenshots/dashboard.png") + + dashboard_errors = [e for e in console_errors if "dashboard" in e.lower()] + issues["dashboard"] = { + "url": page.url, + "title": page.title(), + "console_errors": dashboard_errors, + "has_content": len(page.content()) > 1000 + } + print(f" 标题: {page.title()}") + print(f" 控制台错误数: {len(dashboard_errors)}") + + # 2. Agents 页面 + print("\n2. 测试 Agents 页面...") + page.goto(f"{BASE_URL}/#/agents") + page.wait_for_load_state("networkidle", timeout=10000) + time.sleep(2) # 等待动态内容加载 + page.screenshot(path="D:/ccProg/swarm/test_screenshots/agents.png") + + # 检查 agents/control API 调用 + agents_errors = [e for e in console_errors if "agent" in e.lower()] + issues["agents"] = { + "url": page.url, + "console_errors": agents_errors, + "has_agent_cards": page.locator('[class*="agent"]').count() > 0 + } + print(f" 控制台错误数: {len(agents_errors)}") + + # 3. Meetings 页面 + print("\n3. 测试 Meetings 页面...") + page.goto(f"{BASE_URL}/#/meetings") + page.wait_for_load_state("networkidle", timeout=10000) + time.sleep(2) + page.screenshot(path="D:/ccProg/swarm/test_screenshots/meetings.png") + + meeting_errors = [e for e in console_errors if "meeting" in e.lower()] + issues["meetings"] = { + "url": page.url, + "console_errors": meeting_errors + } + print(f" 控制台错误数: {len(meeting_errors)}") + + # 4. Resources 页面 + print("\n4. 测试 Resources 页面...") + page.goto(f"{BASE_URL}/#/resources") + page.wait_for_load_state("networkidle", timeout=10000) + time.sleep(2) + page.screenshot(path="D:/ccProg/swarm/test_screenshots/resources.png") + + resource_errors = [e for e in console_errors if "resource" in e.lower()] + issues["resources"] = { + "url": page.url, + "console_errors": resource_errors + } + print(f" 控制台错误数: {len(resource_errors)}") + + # 5. Workflow 页面 + print("\n5. 测试 Workflow 页面...") + page.goto(f"{BASE_URL}/#/workflow") + page.wait_for_load_state("networkidle", timeout=10000) + time.sleep(2) + page.screenshot(path="D:/ccProg/swarm/test_screenshots/workflow.png") + + workflow_errors = [e for e in console_errors if "workflow" in e.lower()] + issues["workflow"] = { + "url": page.url, + "console_errors": workflow_errors + } + print(f" 控制台错误数: {len(workflow_errors)}") + + except Exception as e: + print(f" 错误: {e}") + issues["error"] = str(e) + + # 输出所有控制台错误 + print("\n=== 所有控制台错误 ===") + for error in console_errors: + print(f" - {error}") + + browser.close() + + return issues + +if __name__ == "__main__": + import os + os.makedirs("D:/ccProg/swarm/test_screenshots", exist_ok=True) + + api_results = test_api_endpoints() + frontend_issues = test_frontend_pages() + + # 保存结果 + with open("D:/ccProg/swarm/test_results.json", "w") as f: + json.dump({ + "api": api_results, + "frontend": frontend_issues + }, f, indent=2) + + print("\n=== 测试结果已保存 ===") + print(" 截图: D:/ccProg/swarm/test_screenshots/") + print(" 结果: D:/ccProg/swarm/test_results.json")