diff --git a/src/web-server/routes.ts b/src/web-server/routes.ts index 9e1cd207..4f2797f7 100644 --- a/src/web-server/routes.ts +++ b/src/web-server/routes.ts @@ -44,7 +44,7 @@ import { } from '../cliproxy/account-manager'; import type { CLIProxyProvider } from '../cliproxy/types'; import { getClaudeEnvVars } from '../cliproxy/config-generator'; -import { getProxyStatus as getProxyProcessStatus } from '../cliproxy/session-tracker'; +import { getProxyStatus as getProxyProcessStatus, stopProxy } from '../cliproxy/session-tracker'; import { ensureCliproxyService } from '../cliproxy/service-manager'; // Unified config imports import { @@ -1389,6 +1389,19 @@ apiRoutes.post('/cliproxy/proxy-start', async (_req: Request, res: Response): Pr } }); +/** + * POST /api/cliproxy/proxy-stop - Stop the CLIProxy service + * Returns: { stopped, pid?, sessionCount?, error? } + */ +apiRoutes.post('/cliproxy/proxy-stop', (_req: Request, res: Response): void => { + try { + const result = stopProxy(); + res.json(result); + } catch (error) { + res.status(500).json({ error: (error as Error).message }); + } +}); + /** * GET /api/cliproxy/models - Get available models from CLIProxyAPI * Returns: { models: CliproxyModel[], byCategory: Record, totalCount: number } diff --git a/ui/src/components/proxy-status-widget.tsx b/ui/src/components/proxy-status-widget.tsx index a85981c2..946d1cff 100644 --- a/ui/src/components/proxy-status-widget.tsx +++ b/ui/src/components/proxy-status-widget.tsx @@ -1,13 +1,13 @@ /** * Proxy Status Widget * - * Displays CLIProxy process status with start button for recovery. + * Displays CLIProxy process status with start/stop/restart controls. * Shows: running state, port, session count, uptime. */ -import { Activity, Power, RefreshCw, Clock, Users } from 'lucide-react'; +import { Activity, Power, RefreshCw, Clock, Users, Square, RotateCw } from 'lucide-react'; import { Button } from '@/components/ui/button'; -import { useProxyStatus, useStartProxy } from '@/hooks/use-cliproxy'; +import { useProxyStatus, useStartProxy, useStopProxy } from '@/hooks/use-cliproxy'; import { cn } from '@/lib/utils'; function formatUptime(startedAt?: string): string { @@ -28,8 +28,18 @@ function formatUptime(startedAt?: string): string { export function ProxyStatusWidget() { const { data: status, isLoading } = useProxyStatus(); const startProxy = useStartProxy(); + const stopProxy = useStopProxy(); const isRunning = status?.running ?? false; + const isActioning = startProxy.isPending || stopProxy.isPending; + + // Restart = stop then start + const handleRestart = async () => { + await stopProxy.mutateAsync(); + // Small delay to ensure port is released + await new Promise((r) => setTimeout(r, 500)); + startProxy.mutate(); + }; return (
{isRunning && status ? ( -
- Port {status.port} - {status.sessionCount !== undefined && status.sessionCount > 0 && ( - - - {status.sessionCount} session{status.sessionCount !== 1 ? 's' : ''} - - )} - {status.startedAt && ( - - - {formatUptime(status.startedAt)} - - )} -
+ <> +
+ Port {status.port} + {status.sessionCount !== undefined && status.sessionCount > 0 && ( + + + {status.sessionCount} session{status.sessionCount !== 1 ? 's' : ''} + + )} + {status.startedAt && ( + + + {formatUptime(status.startedAt)} + + )} +
+ {/* Control buttons when running */} +
+ + +
+ ) : (
Not running diff --git a/ui/src/hooks/use-cliproxy.ts b/ui/src/hooks/use-cliproxy.ts index 9f23842e..65a5be2f 100644 --- a/ui/src/hooks/use-cliproxy.ts +++ b/ui/src/hooks/use-cliproxy.ts @@ -240,3 +240,24 @@ export function useStartProxy() { }, }); } + +export function useStopProxy() { + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: () => api.cliproxy.proxyStop(), + onSuccess: (data) => { + queryClient.invalidateQueries({ queryKey: ['proxy-status'] }); + if (data.stopped) { + toast.success( + `CLIProxy stopped${data.sessionCount ? ` (${data.sessionCount} session(s) disconnected)` : ''}` + ); + } else { + toast.error(data.error || 'Failed to stop CLIProxy'); + } + }, + onError: (error: Error) => { + toast.error(error.message); + }, + }); +} diff --git a/ui/src/lib/api-client.ts b/ui/src/lib/api-client.ts index 9732272b..5d5096a5 100644 --- a/ui/src/lib/api-client.ts +++ b/ui/src/lib/api-client.ts @@ -182,6 +182,14 @@ export interface ProxyStartResult { error?: string; } +/** Result from stopping proxy service */ +export interface ProxyStopResult { + stopped: boolean; + pid?: number; + sessionCount?: number; + error?: string; +} + // API export const api = { profiles: { @@ -216,6 +224,7 @@ export const api = { // Proxy process status and control proxyStatus: () => request('/cliproxy/proxy-status'), proxyStart: () => request('/cliproxy/proxy-start', { method: 'POST' }), + proxyStop: () => request('/cliproxy/proxy-stop', { method: 'POST' }), // Stats and models for Overview tab stats: () => request<{ usage: Record }>('/cliproxy/usage'),