mirror of
https://github.com/tiennm99/ccs.git
synced 2026-10-12 18:15:48 +00:00
feat(cliproxy): add dashboard UI parity for version stability
Add version stability warnings to Dashboard API and UI: - Extend /api/cliproxy/update-check with isStable, maxStableVersion, stabilityMessage - Health check shows warning status for v81+ installations - UI header displays version badge with amber warning for unstable Closes: relates to #269
This commit is contained in:
1 parent
212aef81bc
commit
c5621dab51
3 files changed
+75
-3
No files matched your search
@@ -8,7 +8,7 @@
|
|||||||
import { info, warn } from '../utils/ui';
|
import { info, warn } from '../utils/ui';
|
||||||
import { getBinDir, CLIPROXY_DEFAULT_PORT } from './config-generator';
|
import { getBinDir, CLIPROXY_DEFAULT_PORT } from './config-generator';
|
||||||
import { BinaryInfo, BinaryManagerConfig } from './types';
|
import { BinaryInfo, BinaryManagerConfig } from './types';
|
||||||
import { CLIPROXY_FALLBACK_VERSION } from './platform-detector';
|
import { CLIPROXY_FALLBACK_VERSION, CLIPROXY_MAX_STABLE_VERSION } from './platform-detector';
|
||||||
import { isProxyRunning, stopProxy } from './services/proxy-lifecycle-service';
|
import { isProxyRunning, stopProxy } from './services/proxy-lifecycle-service';
|
||||||
import { waitForPortFree } from '../utils/port-utils';
|
import { waitForPortFree } from '../utils/port-utils';
|
||||||
import {
|
import {
|
||||||
@@ -151,11 +151,29 @@ export interface CliproxyUpdateCheckResult {
|
|||||||
latestVersion: string;
|
latestVersion: string;
|
||||||
fromCache: boolean;
|
fromCache: boolean;
|
||||||
checkedAt: number;
|
checkedAt: number;
|
||||||
|
// Stability fields
|
||||||
|
isStable: boolean;
|
||||||
|
maxStableVersion: string;
|
||||||
|
stabilityMessage?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Check for CLIProxyAPI binary updates */
|
/** Check for CLIProxyAPI binary updates */
|
||||||
export async function checkCliproxyUpdate(): Promise<CliproxyUpdateCheckResult> {
|
export async function checkCliproxyUpdate(): Promise<CliproxyUpdateCheckResult> {
|
||||||
return new BinaryManager().checkForUpdates();
|
const result = await new BinaryManager().checkForUpdates();
|
||||||
|
|
||||||
|
// Import isNewerVersion for stability check
|
||||||
|
const { isNewerVersion } = await import('./binary/version-checker');
|
||||||
|
const isStable = !isNewerVersion(result.currentVersion, CLIPROXY_MAX_STABLE_VERSION);
|
||||||
|
const stabilityMessage = isStable
|
||||||
|
? undefined
|
||||||
|
: `v${result.currentVersion} has known stability issues. Max stable: v${CLIPROXY_MAX_STABLE_VERSION}`;
|
||||||
|
|
||||||
|
return {
|
||||||
|
...result,
|
||||||
|
isStable,
|
||||||
|
maxStableVersion: CLIPROXY_MAX_STABLE_VERSION,
|
||||||
|
stabilityMessage,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Re-export version pin functions
|
// Re-export version pin functions
|
||||||
|
|||||||
@@ -15,6 +15,8 @@ import {
|
|||||||
} from '../../cliproxy';
|
} from '../../cliproxy';
|
||||||
import { getPortProcess, isCLIProxyProcess } from '../../utils/port-utils';
|
import { getPortProcess, isCLIProxyProcess } from '../../utils/port-utils';
|
||||||
import type { HealthCheck } from './types';
|
import type { HealthCheck } from './types';
|
||||||
|
import { CLIPROXY_MAX_STABLE_VERSION } from '../../cliproxy/platform-detector';
|
||||||
|
import { isNewerVersion } from '../../cliproxy/binary/version-checker';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check CLIProxy binary installation
|
* Check CLIProxy binary installation
|
||||||
@@ -23,6 +25,21 @@ export function checkCliproxyBinary(): HealthCheck {
|
|||||||
if (isCLIProxyInstalled()) {
|
if (isCLIProxyInstalled()) {
|
||||||
const version = getInstalledCliproxyVersion();
|
const version = getInstalledCliproxyVersion();
|
||||||
const binaryPath = getCLIProxyPath();
|
const binaryPath = getCLIProxyPath();
|
||||||
|
|
||||||
|
// Check if version exceeds stable cap
|
||||||
|
const isUnstable = isNewerVersion(version, CLIPROXY_MAX_STABLE_VERSION);
|
||||||
|
|
||||||
|
if (isUnstable) {
|
||||||
|
return {
|
||||||
|
id: 'cliproxy-binary',
|
||||||
|
name: 'CLIProxy Binary',
|
||||||
|
status: 'warning',
|
||||||
|
message: `v${version} (unstable)`,
|
||||||
|
details: binaryPath,
|
||||||
|
fix: `Downgrade: ccs cliproxy install ${CLIPROXY_MAX_STABLE_VERSION.replace(/-\d+$/, '')}`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: 'cliproxy-binary',
|
id: 'cliproxy-binary',
|
||||||
name: 'CLIProxy Binary',
|
name: 'CLIProxy Binary',
|
||||||
|
|||||||
@@ -6,11 +6,17 @@
|
|||||||
import { useState, useEffect } from 'react';
|
import { useState, useEffect } from 'react';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { Badge } from '@/components/ui/badge';
|
import { Badge } from '@/components/ui/badge';
|
||||||
import { RefreshCw, Loader2 } from 'lucide-react';
|
import { RefreshCw, Loader2, AlertTriangle } from 'lucide-react';
|
||||||
import { useCliproxyAuth } from '@/hooks/use-cliproxy';
|
import { useCliproxyAuth } from '@/hooks/use-cliproxy';
|
||||||
import { useCliproxyAuthFlow } from '@/hooks/use-cliproxy-auth-flow';
|
import { useCliproxyAuthFlow } from '@/hooks/use-cliproxy-auth-flow';
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
|
|
||||||
|
interface VersionInfo {
|
||||||
|
currentVersion: string;
|
||||||
|
isStable: boolean;
|
||||||
|
stabilityMessage?: string;
|
||||||
|
}
|
||||||
|
|
||||||
interface LoginButtonProps {
|
interface LoginButtonProps {
|
||||||
provider: string;
|
provider: string;
|
||||||
displayName: string;
|
displayName: string;
|
||||||
@@ -110,6 +116,22 @@ export function CliproxyHeader({
|
|||||||
const { data: authData } = useCliproxyAuth();
|
const { data: authData } = useCliproxyAuth();
|
||||||
const { provider: authProvider, isAuthenticating, startAuth } = useCliproxyAuthFlow();
|
const { provider: authProvider, isAuthenticating, startAuth } = useCliproxyAuthFlow();
|
||||||
const lastUpdatedText = useRelativeTime(lastUpdated);
|
const lastUpdatedText = useRelativeTime(lastUpdated);
|
||||||
|
const [versionInfo, setVersionInfo] = useState<VersionInfo | null>(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
fetch('/api/cliproxy/update-check')
|
||||||
|
.then((res) => (res.ok ? res.json() : null))
|
||||||
|
.then((data) => {
|
||||||
|
if (data) {
|
||||||
|
setVersionInfo({
|
||||||
|
currentVersion: data.currentVersion,
|
||||||
|
isStable: data.isStable,
|
||||||
|
stabilityMessage: data.stabilityMessage,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(() => {}); // Silently fail
|
||||||
|
}, []);
|
||||||
|
|
||||||
const providers = [
|
const providers = [
|
||||||
{ id: 'claude', displayName: 'Claude' },
|
{ id: 'claude', displayName: 'Claude' },
|
||||||
@@ -170,6 +192,21 @@ export function CliproxyHeader({
|
|||||||
{isRunning ? 'Running' : 'Offline'}
|
{isRunning ? 'Running' : 'Offline'}
|
||||||
</Badge>
|
</Badge>
|
||||||
|
|
||||||
|
{versionInfo && (
|
||||||
|
<Badge
|
||||||
|
variant={versionInfo.isStable ? 'secondary' : 'destructive'}
|
||||||
|
className={cn(
|
||||||
|
'gap-1.5',
|
||||||
|
!versionInfo.isStable &&
|
||||||
|
'bg-amber-500/20 text-amber-600 dark:text-amber-400 border-amber-500/30'
|
||||||
|
)}
|
||||||
|
title={versionInfo.stabilityMessage}
|
||||||
|
>
|
||||||
|
{!versionInfo.isStable && <AlertTriangle className="w-3 h-3" />}v
|
||||||
|
{versionInfo.currentVersion}
|
||||||
|
</Badge>
|
||||||
|
)}
|
||||||
|
|
||||||
{lastUpdatedText && (
|
{lastUpdatedText && (
|
||||||
<span className="text-xs text-muted-foreground">{lastUpdatedText}</span>
|
<span className="text-xs text-muted-foreground">{lastUpdatedText}</span>
|
||||||
)}
|
)}
|
||||||
|
|||||||
Reference in new issue
Block a user