feat(cliproxy): add version management UI with install/restart controls

Implements comprehensive version management for CLIProxyAPI Plus:

Backend APIs:
- GET /versions: fetch available versions from GitHub releases
- POST /install: install specific version with force flag for unstable
- POST /restart: restart proxy without version change

Frontend:
- 4-button layout: Restart, Update/Downgrade, Stop, Settings gear
- Collapsible version picker with dropdown + manual input
- Confirmation dialog for unstable versions (>6.6.80)
- Progressive disclosure (version picker hidden by default)

Ref: plans/260105-1250-cliproxy-version-management/
This commit is contained in:
kaitranntt
2026-01-05 13:12:46 -05:00
parent 8a56a43989
commit a69b2e9d10
7 changed files with 569 additions and 15 deletions
+56
View File
@@ -304,3 +304,59 @@ export function useCliproxyUpdateCheck() {
refetchOnWindowFocus: false, // Don't refresh on window focus (save API calls)
});
}
// ==================== Version Management ====================
export function useCliproxyVersions() {
return useQuery({
queryKey: ['cliproxy-versions'],
queryFn: () => api.cliproxy.versions(),
staleTime: 60 * 60 * 1000, // 1 hour (matches backend cache)
refetchOnWindowFocus: false,
});
}
export function useInstallVersion() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: ({ version, force }: { version: string; force?: boolean }) =>
api.cliproxy.install(version, force),
onSuccess: (data) => {
if (data.requiresConfirmation) {
// Don't show toast - let caller handle confirmation dialog
return;
}
queryClient.invalidateQueries({ queryKey: ['cliproxy-versions'] });
queryClient.invalidateQueries({ queryKey: ['cliproxy-update-check'] });
queryClient.invalidateQueries({ queryKey: ['proxy-status'] });
if (data.success) {
toast.success(data.message || `Installed v${data.version}`);
} else {
toast.error(data.error || 'Installation failed');
}
},
onError: (error: Error) => {
toast.error(error.message);
},
});
}
export function useRestartProxy() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: () => api.cliproxy.restart(),
onSuccess: (data) => {
queryClient.invalidateQueries({ queryKey: ['proxy-status'] });
if (data.success) {
toast.success(`Proxy restarted on port ${data.port}`);
} else {
toast.error(data.error || 'Restart failed');
}
},
onError: (error: Error) => {
toast.error(error.message);
},
});
}