'use client'; import React, { useEffect, useState } from 'react'; import { Blocks, RefreshCw, Loader2, AlertCircle, Bot, Terminal, Wrench, ChevronDown, ChevronRight, Globe, FolderOpen, } from 'lucide-react'; import { listPlugins } from '@/lib/api'; import { Button } from '@/components/ui/button'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Badge } from '@/components/ui/badge'; import type { PluginInfo } from '@/types'; export default function PluginsPage() { const [plugins, setPlugins] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const load = async () => { setLoading(true); setError(null); try { const data = await listPlugins(); setPlugins(Array.isArray(data) ? data : []); } catch (err: any) { setError(err.message || '加载插件失败'); } finally { setLoading(false); } }; useEffect(() => { load(); }, []); if (loading) { return (
); } return (
{/* Page header */}

插件

已安装位置:全局插件目录或当前 workspace 的 plugins/

{/* Error */} {error && (
{error}
)} {/* Empty state */} {!error && plugins.length === 0 && (

还没有安装任何插件

把插件目录放到全局插件目录或当前 workspace 的 plugins/, 然后重启 Boardware Agent Sandbox。

)} {/* Plugin cards */}
{plugins.map((plugin) => ( ))}
); } function PluginCard({ plugin }: { plugin: PluginInfo }) { const [agentsOpen, setAgentsOpen] = useState(true); const [commandsOpen, setCommandsOpen] = useState(true); const [skillsOpen, setSkillsOpen] = useState(false); const totalItems = plugin.agents.length + plugin.commands.length + plugin.skills.length; return (
{plugin.name}
{plugin.description && (

{plugin.description}

)}
{/* Summary chips */}
{plugin.agents.length > 0 && ( {plugin.agents.length} 个智能体 )} {plugin.commands.length > 0 && ( {plugin.commands.length} 条命令 )} {plugin.skills.length > 0 && ( {plugin.skills.length} 个技能 )}
{totalItems > 0 && ( {/* Agents */} {plugin.agents.length > 0 && (
} label="智能体" count={plugin.agents.length} open={agentsOpen} onToggle={() => setAgentsOpen((v) => !v)} >
{plugin.agents.map((agent) => (
{agent.name}

{agent.description || '—'}

{agent.model && ( {agent.model} )}
))}
)} {/* Commands */} {plugin.commands.length > 0 && (
} label="命令" count={plugin.commands.length} open={commandsOpen} onToggle={() => setCommandsOpen((v) => !v)} >
{plugin.commands.map((cmd) => (
/{cmd.name} {cmd.argument_hint && ( {cmd.argument_hint} )}

{cmd.description || '—'}

))}
)} {/* Skills */} {plugin.skills.length > 0 && (
} label="技能" count={plugin.skills.length} open={skillsOpen} onToggle={() => setSkillsOpen((v) => !v)} >
{plugin.skills.map((skill) => ( {skill} ))}
)}
)}
); } function SourceBadge({ source }: { source: 'global' | 'workspace' }) { if (source === 'workspace') { return ( 工作区 ); } return ( 全局 ); } function Section({ icon, label, count, open, onToggle, children, }: { icon: React.ReactNode; label: string; count: number; open: boolean; onToggle: () => void; children: React.ReactNode; }) { return (
{open && children}
); }