'use client'; import { useEffect, useState } from 'react'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger, DialogFooter } from '@/components/ui/dialog'; import { Badge } from '@/components/ui/badge'; import { Alert, AlertDescription } from '@/components/ui/alert'; import { Key, Plus, RefreshCw, Check, X, Edit, Trash2 } from 'lucide-react'; interface Provider { id: string; name: string; type: string; hasKey: boolean; key?: string; } export function ProviderManager() { const [providers, setProviders] = useState([]); const [loading, setLoading] = useState(true); const [dialogOpen, setDialogOpen] = useState(false); const [editingProvider, setEditingProvider] = useState(null); const [apiKey, setApiKey] = useState(''); const [saving, setSaving] = useState(false); const [message, setMessage] = useState<{ type: 'success' | 'error'; text: string } | null>(null); const fetchProviders = async () => { setLoading(true); try { const response = await fetch('/api/providers'); const data = await response.json(); setProviders(data.providers || []); } catch (error) { console.error('Failed to fetch providers:', error); setMessage({ type: 'error', text: 'Failed to load providers' }); } finally { setLoading(false); } }; useEffect(() => { fetchProviders(); }, []); const handleEdit = (provider: Provider) => { setEditingProvider(provider); setApiKey(provider.key || ''); setDialogOpen(true); }; const handleAddNew = () => { setEditingProvider(null); setApiKey(''); setDialogOpen(true); }; const handleSave = async () => { if (!editingProvider) { setMessage({ type: 'error', text: 'Please select a provider to edit' }); return; } setSaving(true); setMessage(null); try { const response = await fetch('/api/providers', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ providerId: editingProvider.id, apiKey, }), }); const result = await response.json(); if (result.success) { setMessage({ type: 'success', text: result.message }); setDialogOpen(false); fetchProviders(); } else { setMessage({ type: 'error', text: result.message || 'Failed to update API key' }); } } catch (error) { setMessage({ type: 'error', text: 'Failed to update API key' }); } finally { setSaving(false); } }; const groupedProviders = providers.reduce((acc, provider) => { if (!acc[provider.type]) { acc[provider.type] = []; } acc[provider.type].push(provider); return acc; }, {} as Record); return (
{/* Header Actions */}
{/* Message */} {message && ( {message.type === 'success' ? ( ) : ( )} {message.text} )} {/* Providers List */} {loading ? (
) : Object.keys(groupedProviders).length === 0 ? (

No providers found

) : (
{Object.entries(groupedProviders).map(([type, typeProviders]) => ( {type} {typeProviders.length}
{typeProviders.map((provider) => (

{provider.name}

{provider.id}

{provider.hasKey ? 'Configured' : 'No Key'}
))}
))}
)} {/* Edit/Add Dialog */} {editingProvider ? `Edit ${editingProvider.name}` : 'Add Provider'}
{editingProvider ? ( <>
) : ( To add a new provider, please first add it via the OpenClaw CLI configuration. )} {editingProvider && (
setApiKey(e.target.value)} placeholder="Enter API key..." className="bg-zinc-800 border-zinc-700 focus:border-orange-500" />

The key will be masked after saving.

)}
{editingProvider && ( )}
); }