/** * API Keys management tab — extracted from Settings.tsx (cq-002) */ import React, { useState } from 'react'; import { getKeys, createKey, revokeKey, type ApiKeyInfo, type ApiKeyCreated, } from '../../api/client'; import { useApi } from '../../hooks/useApi'; // ─── API Key Scopes ───────────────────────────────────────── const AVAILABLE_SCOPES = [ '*', 'events:write', 'events:read', 'sessions:read', 'agents:read', 'stats:read', ] as const; type Scope = (typeof AVAILABLE_SCOPES)[number]; // ─── Create Key Form ──────────────────────────────────────── interface CreateKeyFormProps { onCreated: (response: ApiKeyCreated) => void; onCancel: () => void; } function CreateKeyForm({ onCreated, onCancel }: CreateKeyFormProps): React.ReactElement { const [name, setName] = useState(''); const [scopes, setScopes] = useState(['*']); const [submitting, setSubmitting] = useState(false); const [error, setError] = useState(null); const toggleScope = (scope: Scope) => { if (scope === '*') { setScopes(['*']); return; } const without = scopes.filter((s) => s !== '*' && s !== scope); if (scopes.includes(scope)) { setScopes(without.length === 0 ? ['*'] : without); } else { setScopes([...without, scope]); } }; const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); if (!name.trim()) return; setSubmitting(true); setError(null); try { const resp = await createKey(name.trim(), scopes); onCreated(resp); } catch (err) { setError(err instanceof Error ? err.message : String(err)); } finally { setSubmitting(false); } }; return (

Create API Key

setName(e.target.value)} placeholder="e.g. production-agent" className="mt-1 w-full rounded border border-gray-300 px-3 py-1.5 text-sm focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500" />
Scopes
{AVAILABLE_SCOPES.map((scope) => ( ))}
{error && (

{error}

)}
); } // ─── New Key Display ──────────────────────────────────────── function NewKeyDisplay({ response, onDismiss, }: { response: ApiKeyCreated; onDismiss: () => void; }): React.ReactElement { const [copied, setCopied] = useState(false); const copyKey = async () => { await navigator.clipboard.writeText(response.key); setCopied(true); setTimeout(() => setCopied(false), 2000); }; return (
⚠️

Save your API key

This is the only time you will see this key. Copy it now and store it securely.

{response.key}
Name: {response.name} · Scopes: {response.scopes.join(', ')}
); } // ─── Revoke Confirmation Dialog ───────────────────────────── function RevokeDialog({ keyInfo, onConfirm, onCancel, }: { keyInfo: ApiKeyInfo; onConfirm: () => Promise; onCancel: () => void; }): React.ReactElement { const [revoking, setRevoking] = useState(false); const [error, setError] = useState(null); const handleRevoke = async () => { setRevoking(true); setError(null); try { await onConfirm(); } catch (err) { setError(err instanceof Error ? err.message : String(err)); setRevoking(false); } }; return (

Revoke API Key

Are you sure you want to revoke {keyInfo.name}? This action cannot be undone. Any agents using this key will lose access immediately.

{error && (

Failed to revoke key: {error}

)}
); } // ─── API Keys Tab ─────────────────────────────────────────── export function ApiKeysTab(): React.ReactElement { const { data: keys, loading, error, refetch } = useApi(() => getKeys(), []); const [showCreate, setShowCreate] = useState(false); const [newKey, setNewKey] = useState(null); const [revokeTarget, setRevokeTarget] = useState(null); const handleCreated = (resp: ApiKeyCreated) => { setNewKey(resp); setShowCreate(false); refetch(); }; const handleRevoke = async () => { if (!revokeTarget) return; await revokeKey(revokeTarget.id); setRevokeTarget(null); refetch(); }; const activeKeys = (keys ?? []).filter((k) => !k.revokedAt); const revokedKeys = (keys ?? []).filter((k) => k.revokedAt); return (
{newKey && setNewKey(null)} />} {showCreate ? ( setShowCreate(false)} /> ) : ( )} {error && (
{error}
)} {loading && !keys && (

Loading API keys…

)} {activeKeys.length > 0 && (
{activeKeys.map((key) => ( ))}
Name Scopes Created Last Used Actions
{key.name}
{key.scopes.map((s) => ( {s} ))}
{new Date(key.createdAt).toLocaleDateString()} {key.lastUsedAt ? new Date(key.lastUsedAt).toLocaleDateString() : 'Never'}
)} {keys && activeKeys.length === 0 && !showCreate && !newKey && (

No API keys

Create an API key to authenticate agents

)} {revokedKeys.length > 0 && (
Revoked Keys ({revokedKeys.length})
{revokedKeys.map((key) => (
{key.name} Revoked {key.revokedAt ? new Date(key.revokedAt).toLocaleDateString() : ''}
))}
)} {revokeTarget && ( setRevokeTarget(null)} /> )}
); }