import { useState, useEffect } from "react"; import type { Member, Label } from "@/lib/api"; import { updateMember, regenerateToken, revokeToken, getLabels } from "@/lib/api"; interface Props { agent: Member; onClose: () => void; onUpdated: (agent: Member) => void; } export default function AgentModal({ agent, onClose, onUpdated }: Props) { const [name, setName] = useState(agent.name); const [slug, setSlug] = useState(agent.slug); const [slugError, setSlugError] = useState(""); const [capabilities, setCapabilities] = useState( agent.agent_config?.capabilities?.join(", ") || "" ); const [chatListen, setChatListen] = useState(agent.agent_config?.chat_listen || "mentions"); const [taskListen, setTaskListen] = useState(agent.agent_config?.task_listen || "assigned"); const [prompt, setPrompt] = useState(agent.agent_config?.prompt || ""); const [model, setModel] = useState(agent.agent_config?.model || ""); const [saving, setSaving] = useState(false); const [currentToken, setCurrentToken] = useState(agent.token || null); const [copied, setCopied] = useState(false); const [confirmRegen, setConfirmRegen] = useState(false); const [confirmRevoke, setConfirmRevoke] = useState(false); const [allLabels, setAllLabels] = useState([]); const [agentLabels, setAgentLabels] = useState(agent.agent_config?.labels || []); useEffect(() => { getLabels().then(setAllLabels).catch(() => {}); }, []); const handleSave = async () => { setSaving(true); try { const caps = capabilities.split(",").map((c) => c.trim()).filter(Boolean); const trimmedSlug = slug.trim().toLowerCase().replace(/[^a-z0-9_-]/g, ''); if (!trimmedSlug) { setSlugError("Slug не может быть пустым"); return; } const updated = await updateMember(agent.id, { name: name.trim(), slug: trimmedSlug !== agent.slug ? trimmedSlug : undefined, agent_config: { capabilities: caps, labels: agentLabels, chat_listen: chatListen, task_listen: taskListen, prompt: prompt.trim() || null, model: model.trim() || null, }, }); onUpdated(updated); onClose(); } catch (e: any) { if (e?.response?.status === 409) { setSlugError("Этот slug уже занят"); } else { console.error(e); } } finally { setSaving(false); } }; const handleRegenerate = async () => { try { const { token } = await regenerateToken(agent.id); setCurrentToken(token); setConfirmRegen(false); } catch (e) { console.error(e); } }; const handleRevoke = async () => { try { await revokeToken(agent.id); setCurrentToken(null); setConfirmRevoke(false); } catch (e) { console.error(e); } }; const copyToken = () => { if (currentToken) { navigator.clipboard.writeText(currentToken); setCopied(true); setTimeout(() => setCopied(false), 2000); } }; return (
e.stopPropagation()} >

{agent.name}

@{agent.slug}
{agent.status === "online" ? "online" : "offline"}
setName(e.target.value)} className="w-full px-3 py-2 bg-[var(--bg)] border border-[var(--border)] rounded-lg outline-none focus:border-[var(--accent)] text-sm" />
{ setSlug(e.target.value); setSlugError(""); }} className={`w-full px-3 py-2 bg-[var(--bg)] border rounded-lg outline-none text-sm ${slugError ? 'border-red-500' : 'border-[var(--border)] focus:border-[var(--accent)]'}`} placeholder="agent-slug" /> {slugError &&
{slugError}
}
setCapabilities(e.target.value)} className="w-full px-3 py-2 bg-[var(--bg)] border border-[var(--border)] rounded-lg outline-none focus:border-[var(--accent)] text-sm" placeholder="code, review, deploy" />
{/* Labels */} {allLabels.length > 0 && (
{allLabels.map((l) => ( ))}

Для авто-назначения: лейблы задачи ∩ лейблы агента

)}