import { useEffect, useMemo, useRef, useState } from 'react'; import { useForm } from 'react-hook-form'; import { z } from 'zod'; import { zodResolver } from '@hookform/resolvers/zod'; import { useMutation, useQuery, useQueryClient, } from '@tanstack/react-query'; import type { AuthResponse, CompleteCodexManualLoginInput, LoginInput, RegisterInput, StartCodexLoginInput, } from '@codexdash/shared-types'; import { Activity, CirclePlus, ExternalLink, Gauge, Link as LinkIcon, LoaderCircle, LogOut, RefreshCw, ShieldCheck, Trash2, Waypoints, } from 'lucide-react'; import { toast, Toaster } from 'sonner'; import { api } from '@/lib/api'; import { clearToken, getToken, setToken } from '@/lib/storage'; import { flattenNumericMetrics, formatDate, titleizeMetric } from '@/lib/utils'; import { Button } from '@/components/ui/button'; import { Card, CardContent, CardDescription, CardHeader, CardTitle, } from '@/components/ui/card'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; import { Badge } from '@/components/ui/badge'; import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogTrigger, } from '@/components/ui/dialog'; import { Progress } from '@/components/ui/progress'; import { Separator } from '@/components/ui/separator'; import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; import { JsonViewer } from '@/components/json-viewer'; const registerSchema = z.object({ name: z.string().min(2), email: z.email(), password: z.string().min(8), }); const loginSchema = z.object({ email: z.email(), password: z.string().min(8), }); const connectSchema = z.object({ label: z.string().min(2), emailHint: z.string().optional(), }); const manualCallbackSchema = z.object({ callbackUrl: z.string().min(10), }); function AuthShell({ onAuthenticated, }: { onAuthenticated: (response: AuthResponse) => void; }) { const [mode, setMode] = useState<'login' | 'register'>('register'); const schema = mode === 'register' ? registerSchema : loginSchema; const form = useForm<{ name?: string; email: string; password: string }>({ resolver: zodResolver(schema), defaultValues: { email: '', password: '', ...(mode === 'register' ? { name: '' } : {}), }, }); const mutation = useMutation({ mutationFn: async (values: { name?: string; email: string; password: string; }) => { return mode === 'register' ? api.register(values as RegisterInput) : api.login({ email: values.email, password: values.password, } as LoginInput); }, onSuccess: (response) => { setToken(response.token); onAuthenticated(response); toast.success( mode === 'register' ? 'Welcome to CodexDash.' : 'Signed in successfully.', ); }, onError: (error: Error) => toast.error(error.message), }); return (
Mobile-first Codex monitor

CodexDash keeps every Codex account in one gorgeous live dashboard.

Sign into CodexDash, connect multiple OpenAI Codex accounts through a real login flow, and view combined limits, remaining usage, raw API payloads, and per-account drilldowns from a single responsive UI.

{[ { icon: Gauge, title: 'Unified usage', desc: 'Merge multiple OpenAI accounts into one overview.', }, { icon: ShieldCheck, title: 'Stored safely', desc: 'OAuth session data is encrypted at rest.', }, { icon: Activity, title: 'Live detail', desc: 'See refreshed usage plus raw usage payloads.', }, ].map((item) => (
{item.title}
{item.desc}
))}
{mode === 'register' ? 'Create your account' : 'Welcome back'} {mode === 'register' ? 'Start with your CodexDash account, then connect OpenAI Codex logins inside the dashboard.' : 'Log in to continue monitoring your combined Codex usage.'}
mutation.mutate(values))} > {mode === 'register' ? (

{String(form.formState.errors.name?.message ?? '')}

) : null}

{String(form.formState.errors.email?.message ?? '')}

{String(form.formState.errors.password?.message ?? '')}

OpenAI account connection now uses a real sign-in flow based on the Codex client OAuth pattern. After you click connect, CodexDash opens OpenAI login in a popup and can also finish from a pasted callback URL if localhost is unavailable.

); } function ConnectAccountDialog() { const queryClient = useQueryClient(); const [open, setOpen] = useState(false); const [attemptId, setAttemptId] = useState(null); const [authorizeUrl, setAuthorizeUrl] = useState(null); const popupRef = useRef(null); const handledAttemptStatusRef = useRef(null); const form = useForm>({ resolver: zodResolver(connectSchema), defaultValues: { label: '', emailHint: '' }, }); const manualCallbackForm = useForm>({ resolver: zodResolver(manualCallbackSchema), defaultValues: { callbackUrl: '' }, }); const startMutation = useMutation({ mutationFn: api.startCodexLogin, onSuccess: (response) => { setAttemptId(response.attemptId); setAuthorizeUrl(response.authorizeUrl); manualCallbackForm.reset(); popupRef.current = window.open( response.authorizeUrl, 'codexdash-openai-login', 'popup=yes,width=520,height=760', ); if (!popupRef.current) { toast.error('Popup was blocked. Use the fallback link inside the dialog.'); } else { toast.success('Continue the OpenAI sign-in flow in the popup.'); } }, onError: (error: Error) => toast.error(error.message), }); const manualCompleteMutation = useMutation({ mutationFn: ({ callbackUrl, currentAttemptId, }: CompleteCodexManualLoginInput & { currentAttemptId: string }) => api.completeCodexManualLogin(currentAttemptId, { callbackUrl }), onSuccess: () => { toast.success('Processing the pasted OpenAI callback URL…'); void attemptQuery.refetch(); }, onError: (error: Error) => toast.error(error.message), }); const attemptQuery = useQuery({ enabled: Boolean(attemptId), queryKey: ['codex-login-attempt', attemptId], queryFn: () => api.getCodexLoginAttempt(attemptId as string), refetchInterval: (query) => query.state.data?.status === 'pending' ? 2_000 : false, }); const cancelMutation = useMutation({ mutationFn: api.cancelCodexLoginAttempt, onSuccess: () => { toast.success('Login attempt cancelled.'); setAttemptId(null); setAuthorizeUrl(null); manualCallbackForm.reset(); popupRef.current?.close(); }, onError: (error: Error) => toast.error(error.message), }); useEffect(() => { const attempt = attemptQuery.data; if (!attempt) { return; } const statusKey = `${attempt.id}:${attempt.status}:${attempt.completedAt ?? ''}:${attempt.lastError ?? ''}`; if (handledAttemptStatusRef.current === statusKey) { return; } if (attempt.status === 'completed') { handledAttemptStatusRef.current = statusKey; window.setTimeout(() => { toast.success('OpenAI Codex account connected.'); setOpen(false); setAttemptId(null); setAuthorizeUrl(null); form.reset(); manualCallbackForm.reset(); popupRef.current?.close(); void queryClient.invalidateQueries({ queryKey: ['usage-summary'] }); }, 0); return; } if (attempt.status === 'error' || attempt.status === 'expired') { handledAttemptStatusRef.current = statusKey; toast.error(attempt.lastError || 'OpenAI login failed.'); } }, [attemptQuery.data, form, manualCallbackForm, queryClient]); useEffect(() => { function onMessage(event: MessageEvent) { if (event.data?.type !== 'codexdash:oauth-complete') { return; } if (event.data?.attemptId && event.data.attemptId === attemptId) { void attemptQuery.refetch(); } } window.addEventListener('message', onMessage); return () => window.removeEventListener('message', onMessage); }, [attemptId, attemptQuery]); const attempt = attemptQuery.data; const isPendingAttempt = attempt?.status === 'pending'; return ( { setOpen(next); if (!next && isPendingAttempt && attemptId) { cancelMutation.mutate(attemptId); } }} > Connect an OpenAI Codex account Start a real OpenAI sign-in flow. CodexDash opens the official login, completes the Codex-style OAuth callback locally, then stores the encrypted session for future usage refreshes. {attemptId ? (
Waiting for OpenAI login
{attempt?.status === 'completed' ? 'The login finished successfully. Closing this dialog…' : 'Finish the sign-in flow in the popup window. CodexDash will detect the callback automatically.'}
Expires: {formatDate(attempt?.expiresAt ?? null)}
{authorizeUrl ? (
) : null}
Manual fallback

If localhost:1455 is not listening, OpenAI may finish on a browser error page. Copy the full URL from the address bar and paste it below to complete the login manually.

{ if (!attemptId) { return; } manualCompleteMutation.mutate({ callbackUrl: values.callbackUrl, currentAttemptId: attemptId, }); })} >

{String( manualCallbackForm.formState.errors.callbackUrl?.message ?? '', )}

{attempt?.lastError ? (
{attempt.lastError}
) : null}
) : (
startMutation.mutate(values as StartCodexLoginInput) )} >

{String(form.formState.errors.label?.message ?? '')}

CodexDash reuses the Codex public-client login shape discovered in codex-pool, but presents it as an integrated popup flow with a manual pasted-URL fallback instead of asking you to paste cookies manually.
)}
); } function Dashboard() { const queryClient = useQueryClient(); const summaryQuery = useQuery({ queryKey: ['usage-summary'], queryFn: () => api.getUsageSummary(true), }); const userQuery = useQuery({ queryKey: ['me'], queryFn: api.me }); const deleteMutation = useMutation({ mutationFn: api.deleteAccount, onSuccess: () => { toast.success('OpenAI account removed.'); void queryClient.invalidateQueries({ queryKey: ['usage-summary'] }); }, onError: (error: Error) => toast.error(error.message), }); const metricCards = useMemo(() => { return flattenNumericMetrics(summaryQuery.data?.aggregatedUsage).slice(0, 6); }, [summaryQuery.data?.aggregatedUsage]); if (summaryQuery.isLoading || userQuery.isLoading) { return (
Loading CodexDash…
); } if (summaryQuery.isError || userQuery.isError) { return (
Unable to load dashboard {(summaryQuery.error as Error | undefined)?.message ?? (userQuery.error as Error | undefined)?.message}
); } const summary = summaryQuery.data!; const user = userQuery.data!; const firstMetric = metricCards[0]?.value ?? 0; const secondMetric = metricCards[1]?.value ?? 0; const progressValue = firstMetric + secondMetric > 0 ? (firstMetric / (firstMetric + secondMetric)) * 100 : 0; return (
Signed in as {user.name}

CodexDash overview

Combined usage is refreshed by calling Codex usage endpoints for each attached OpenAI account and merging numeric fields into one dashboard.

Unified capacity Fast glance card for the first two numeric metrics extracted from the merged usage payload.
{firstMetric.toLocaleString()}
{titleizeMetric(metricCards[0]?.label ?? 'Primary metric')}
{secondMetric.toLocaleString()}
{titleizeMetric(metricCards[1]?.label ?? 'Secondary metric')}
Accounts: {summary.totals.totalAccounts} Healthy: {summary.totals.activeAccounts} Errors: {summary.totals.erroredAccounts} Updated: {formatDate(summary.refreshedAt)}
{[ { title: 'Connected sessions', value: summary.totals.totalAccounts, tone: 'text-sky-300', }, { title: 'Healthy sessions', value: summary.totals.activeAccounts, tone: 'text-emerald-300', }, { title: 'Errored sessions', value: summary.totals.erroredAccounts, tone: 'text-rose-300', }, ].map((item) => ( {item.title} {item.value.toLocaleString()} ))}
Usage metrics CodexDash extracts numeric leaf nodes from the aggregated usage payload for quick overview cards. {metricCards.length === 0 ? (
No usage data yet. Connect an OpenAI account and complete the sign-in flow to start refreshing.
) : (
{metricCards.map((metric) => (
{titleizeMetric(metric.label)}
{metric.value.toLocaleString()}
))}
)}
Connected OpenAI accounts By default, these accounts are merged into one Codex usage view. Switch tabs to inspect individual account payloads and timestamps. {summary.accounts.length === 0 ? (
No OpenAI accounts connected yet.
) : ( {summary.accounts.map((account) => ( {account.label} ))} {summary.accounts.map((account) => (
{account.label}
{account.providerEmail || account.emailHint || 'No email available yet'}
{account.status}
Auth: {account.authType}
Plan: {account.planType || 'Unknown'}
Provider account:{' '} {account.providerAccountId || 'Unknown'}
Session expires: {formatDate(account.sessionExpiresAt)}
Last synced: {formatDate(account.lastSyncedAt)}
Connected: {formatDate(account.createdAt)}
Error:{' '} {account.lastError || 'None'}
))}
)}
); } export default function App() { const [authenticated, setAuthenticated] = useState(Boolean(getToken())); return (
{authenticated ? ( ) : ( setAuthenticated(true)} /> )}
); }