Files
multiAgentTry/frontend/src/components/BarrierSyncCard.tsx
Claude Code dc398d7c7b 完整实现 Swarm 多智能体协作系统
- 新增 CLIPluginAdapter 统一接口 (backend/app/core/agent_adapter.py)
- 新增 LLM 服务层,支持 Anthropic/OpenAI/DeepSeek/Ollama (backend/app/services/llm_service.py)
- 新增 Agent 执行引擎,支持文件锁自动管理 (backend/app/services/agent_executor.py)
- 新增 NativeLLMAgent 原生 LLM 适配器 (backend/app/adapters/native_llm_agent.py)
- 新增进程管理器 (backend/app/services/process_manager.py)
- 新增 Agent 控制 API (backend/app/routers/agents_control.py)
- 新增 WebSocket 实时通信 (backend/app/routers/websocket.py)
- 更新前端 AgentsPage,支持启动/停止 Agent
- 测试通过:Agent 启动、批量操作、栅栏同步

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 17:32:11 +08:00

280 lines
9.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { Shield, Zap } from "lucide-react";
type NodeState = "ready" | "waiting" | "pending";
interface BarrierNode {
id: string;
name: string;
gradient: string;
state: NodeState;
waitingFor?: string;
}
const nodes: BarrierNode[] = [
{
id: "n1",
name: "CLA",
gradient: "linear-gradient(135deg,#8b5cf6,#6366f1)",
state: "ready",
},
{
id: "n2",
name: "KIM",
gradient: "linear-gradient(135deg,#f59e0b,#d97706)",
state: "waiting",
waitingFor: "架构设计完成",
},
{
id: "n3",
name: "OPC",
gradient: "linear-gradient(135deg,#10b981,#059669)",
state: "ready",
},
{
id: "n4",
name: "USR",
gradient: "linear-gradient(135deg,#f59e0b,#b45309)",
state: "pending",
},
];
const stateColors: Record<NodeState, string> = {
ready: "#00ff9d",
waiting: "#ff9500",
pending: "#374151",
};
const stateLabels: Record<NodeState, string> = {
ready: "READY",
waiting: "WAIT",
pending: "IDLE",
};
const syncPoints = [
{ name: "INIT", completed: true },
{ name: "REVIEW", completed: true },
{ name: "DESIGN", completed: false, active: true },
{ name: "IMPL", completed: false },
{ name: "DEPLOY", completed: false },
];
export function BarrierSyncCard() {
const readyCount = nodes.filter(n => n.state === "ready").length;
return (
<div className="glass-card" style={{ padding: 20, height: "100%", display: "flex", flexDirection: "column" }}>
{/* Header */}
<div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", marginBottom: 14, flexShrink: 0 }}>
<div style={{ display: "flex", alignItems: "center", gap: 10 }}>
<div
style={{
width: 28,
height: 28,
borderRadius: 8,
background: "rgba(0,240,255,0.1)",
border: "1px solid rgba(0,240,255,0.2)",
display: "flex",
alignItems: "center",
justifyContent: "center",
}}
>
<Shield size={14} color="#00f0ff" />
</div>
<span className="card-title"></span>
</div>
<div style={{ display: "flex", alignItems: "center", gap: 6 }}>
<span className="font-mono-code" style={{ fontSize: 11, color: "#ff9500" }}>
{readyCount}/{nodes.length}
</span>
<div
style={{
padding: "3px 10px",
background: "rgba(255,149,0,0.1)",
border: "1px solid rgba(255,149,0,0.3)",
borderRadius: 6,
}}
>
<span className="font-mono-code" style={{ fontSize: 10, color: "#ff9500" }}>
<span className="animate-pulse-fast"></span>
</span>
</div>
</div>
</div>
<div style={{ display: "flex", gap: 20, flex: 1, minHeight: 0 }}>
{/* Left: Agent nodes */}
<div style={{ display: "flex", flexDirection: "column", gap: 8, minWidth: 200 }}>
{nodes.map(node => (
<div
key={node.id}
style={{
display: "flex",
alignItems: "center",
gap: 10,
padding: "8px 12px",
background: "rgba(0,0,0,0.3)",
border: `1px solid ${stateColors[node.state]}30`,
borderRadius: 10,
transition: "all 0.3s ease",
}}
>
<div
className="agent-avatar"
style={{ background: node.gradient, width: 32, height: 32, fontSize: 10 }}
>
{node.name}
</div>
<div style={{ flex: 1, minWidth: 0 }}>
<div className="font-mono-code" style={{ fontSize: 11, color: "#9ca3af" }}>
{node.name === "CLA" ? "Claude Code" : node.name === "KIM" ? "Kimi CLI" : node.name === "OPC" ? "OpenCode" : "Tech Lead"}
</div>
{node.waitingFor && (
<div className="font-mono-code" style={{ fontSize: 9, color: "#ff9500", marginTop: 1 }}>
: {node.waitingFor}
</div>
)}
</div>
<div
className="barrier-node"
style={{
width: 32,
height: 32,
borderColor: stateColors[node.state],
color: stateColors[node.state],
background: `${stateColors[node.state]}10`,
fontSize: 8,
}}
title={stateLabels[node.state]}
>
{node.state === "ready" ? (
<svg width="12" height="12" viewBox="0 0 12 12">
<path d="M2 6L5 9L10 3" stroke="#00ff9d" strokeWidth="1.5" strokeLinecap="round" fill="none" />
</svg>
) : node.state === "waiting" ? (
<Zap size={12} />
) : (
<span style={{ fontSize: 8 }}></span>
)}
</div>
</div>
))}
</div>
{/* Right: Sync points */}
<div style={{ flex: 1, minWidth: 0 }}>
<div className="font-mono-code" style={{ fontSize: 10, color: "#6b7280", marginBottom: 10 }}>
</div>
{/* Progress line */}
<div style={{ position: "relative", marginBottom: 16 }}>
<div
style={{
height: 3,
background: "#111827",
borderRadius: 2,
position: "relative",
overflow: "visible",
}}
>
{/* Completed portion */}
<div
style={{
position: "absolute",
left: 0,
top: 0,
height: "100%",
width: "42%",
background: "linear-gradient(90deg,#00f0ff,#00ff9d)",
borderRadius: 2,
}}
/>
{/* Active flowing portion */}
<div
style={{
position: "absolute",
left: "42%",
top: 0,
height: "100%",
width: "16%",
background: "linear-gradient(90deg,#ff9500,rgba(255,149,0,0.3))",
borderRadius: 2,
overflow: "hidden",
}}
>
<div
style={{
position: "absolute",
inset: 0,
background: "linear-gradient(90deg,transparent,rgba(255,255,255,0.3),transparent)",
animation: "line-flow 1.5s linear infinite",
}}
/>
</div>
{/* Dots */}
{syncPoints.map((sp, i) => {
const left = `${(i / (syncPoints.length - 1)) * 100}%`;
return (
<div
key={sp.name}
style={{
position: "absolute",
top: "50%",
left,
transform: "translate(-50%,-50%)",
width: 12,
height: 12,
borderRadius: "50%",
border: `2px solid ${sp.completed ? "#00ff9d" : sp.active ? "#ff9500" : "#374151"}`,
background: sp.completed ? "#00ff9d" : sp.active ? "#ff9500" : "#111827",
zIndex: 2,
boxShadow: sp.active ? "0 0 8px rgba(255,149,0,0.6)" : sp.completed ? "0 0 6px rgba(0,255,157,0.4)" : "none",
}}
className={sp.active ? "animate-scale-pulse" : ""}
/>
);
})}
</div>
{/* Labels */}
<div style={{ display: "flex", justifyContent: "space-between", marginTop: 12 }}>
{syncPoints.map(sp => (
<div
key={sp.name}
className="font-mono-code"
style={{
fontSize: 9,
color: sp.completed ? "#00ff9d" : sp.active ? "#ff9500" : "#374151",
textAlign: "center",
flex: 1,
}}
>
{sp.name}
</div>
))}
</div>
</div>
{/* Status panel */}
<div
style={{
padding: "10px 12px",
background: "rgba(255,149,0,0.05)",
border: "1px solid rgba(255,149,0,0.15)",
borderRadius: 10,
}}
>
<div className="font-mono-code" style={{ fontSize: 10, color: "#ff9500", marginBottom: 4 }}>
DESIGN
</div>
<div className="font-mono-code" style={{ fontSize: 10, color: "#6b7280" }}>
Agent wait_for_meeting("design_review") · {readyCount}/{nodes.length}
</div>
</div>
</div>
</div>
</div>
);
}