- {/* Login hint banner */}
+ {/* Remote indicator and login hint banner */}
{showLoginHint && !isLoading && (
+ {isRemote && (
+ <>
+
+
Remote
+
|
+ >
+ )}
Key:{' '}
- ccs
+ {authToken && authToken.length > 4
+ ? `***${authToken.slice(-4)}`
+ : authToken || 'ccs'}
)}
diff --git a/ui/src/components/cliproxy/provider-editor/index.tsx b/ui/src/components/cliproxy/provider-editor/index.tsx
index 2d9f14ff..a153dbbd 100644
--- a/ui/src/components/cliproxy/provider-editor/index.tsx
+++ b/ui/src/components/cliproxy/provider-editor/index.tsx
@@ -31,6 +31,7 @@ export function ProviderEditor({
authStatus,
catalog,
logoProvider,
+ isRemoteMode,
onAddAccount,
onSetDefault,
onRemoveAccount,
@@ -124,6 +125,7 @@ export function ProviderEditor({
hasChanges={hasChanges}
isRawJsonValid={isRawJsonValid}
isSaving={saveMutation.isPending}
+ isRemoteMode={isRemoteMode}
onRefetch={refetch}
onSave={() => saveMutation.mutate()}
/>
diff --git a/ui/src/components/cliproxy/provider-editor/provider-editor-header.tsx b/ui/src/components/cliproxy/provider-editor/provider-editor-header.tsx
index f64f9605..3d662811 100644
--- a/ui/src/components/cliproxy/provider-editor/provider-editor-header.tsx
+++ b/ui/src/components/cliproxy/provider-editor/provider-editor-header.tsx
@@ -5,7 +5,7 @@
import { Button } from '@/components/ui/button';
import { Badge } from '@/components/ui/badge';
-import { Save, Loader2, RefreshCw } from 'lucide-react';
+import { Save, Loader2, RefreshCw, Globe } from 'lucide-react';
import { ProviderLogo } from '../provider-logo';
import type { SettingsResponse } from './types';
@@ -18,6 +18,7 @@ interface ProviderEditorHeaderProps {
hasChanges: boolean;
isRawJsonValid: boolean;
isSaving: boolean;
+ isRemoteMode?: boolean;
onRefetch: () => void;
onSave: () => void;
}
@@ -31,6 +32,7 @@ export function ProviderEditorHeader({
hasChanges,
isRawJsonValid,
isSaving,
+ isRemoteMode,
onRefetch,
onSave,
}: ProviderEditorHeaderProps) {
@@ -41,16 +43,31 @@ export function ProviderEditorHeader({
{displayName}
- {data?.path && (
+ {isRemoteMode && (
+
+
+ Remote
+
+ )}
+ {!isRemoteMode && data?.path && (
- {data.path.replace(/^.*\//, '')}
+ {data.path.replace(/^.*[\\/]/, '')}
)}
- {data && (
-
- Last modified: {new Date(data.mtime).toLocaleString()}
+ {isRemoteMode ? (
+
+ Traffic auto-routed to remote server
+ ) : (
+ data && (
+
+ Last modified: {new Date(data.mtime).toLocaleString()}
+
+ )
)}
diff --git a/ui/src/components/cliproxy/provider-editor/types.ts b/ui/src/components/cliproxy/provider-editor/types.ts
index 4d1d87a6..6bea9288 100644
--- a/ui/src/components/cliproxy/provider-editor/types.ts
+++ b/ui/src/components/cliproxy/provider-editor/types.ts
@@ -21,6 +21,8 @@ export interface ProviderEditorProps {
catalog?: ProviderCatalog;
/** Provider type for logo display (defaults to provider) */
logoProvider?: string;
+ /** True if using remote CLIProxy mode (hides local paths) */
+ isRemoteMode?: boolean;
onAddAccount: () => void;
onSetDefault: (accountId: string) => void;
onRemoveAccount: (accountId: string) => void;
diff --git a/ui/src/components/monitoring/proxy-status-widget.tsx b/ui/src/components/monitoring/proxy-status-widget.tsx
index bfa0a2c3..6c4af3b0 100644
--- a/ui/src/components/monitoring/proxy-status-widget.tsx
+++ b/ui/src/components/monitoring/proxy-status-widget.tsx
@@ -3,11 +3,24 @@
*
* Displays CLIProxy process status with start/stop/restart controls.
* Shows: running state, port, session count, uptime, update availability.
+ * In remote mode: shows remote server info instead of local controls.
*/
-import { Activity, Power, RefreshCw, Clock, Users, Square, RotateCw, ArrowUp } from 'lucide-react';
+import {
+ Activity,
+ Power,
+ RefreshCw,
+ Clock,
+ Users,
+ Square,
+ RotateCw,
+ ArrowUp,
+ Globe,
+} from 'lucide-react';
import { Button } from '@/components/ui/button';
import { Badge } from '@/components/ui/badge';
+import { useQuery } from '@tanstack/react-query';
+import { api, type CliproxyServerConfig } from '@/lib/api-client';
import {
useProxyStatus,
useStartProxy,
@@ -48,10 +61,32 @@ export function ProxyStatusWidget() {
const startProxy = useStartProxy();
const stopProxy = useStopProxy();
+ // Fetch cliproxy_server config for remote mode detection
+ const { data: cliproxyConfig } = useQuery
({
+ queryKey: ['cliproxy-server-config'],
+ queryFn: () => api.cliproxyServer.get(),
+ staleTime: 30000, // 30 seconds
+ });
+
+ // Determine if remote mode is enabled
+ const remoteConfig = cliproxyConfig?.remote;
+ const isRemoteMode = remoteConfig?.enabled && remoteConfig?.host;
+
const isRunning = status?.running ?? false;
const isActioning = startProxy.isPending || stopProxy.isPending;
const hasUpdate = updateCheck?.hasUpdate ?? false;
+ // Build remote display info
+ const remoteDisplayHost = isRemoteMode
+ ? (() => {
+ const protocol = remoteConfig.protocol || 'http';
+ const port = remoteConfig.port || (protocol === 'https' ? 443 : 80);
+ const isDefaultPort =
+ (protocol === 'https' && port === 443) || (protocol === 'http' && port === 80);
+ return isDefaultPort ? remoteConfig.host : `${remoteConfig.host}:${port}`;
+ })()
+ : null;
+
// Restart = stop then start
const handleRestart = async () => {
await stopProxy.mutateAsync();
@@ -60,6 +95,43 @@ export function ProxyStatusWidget() {
startProxy.mutate();
};
+ // Remote mode: show remote server info
+ if (isRemoteMode) {
+ return (
+
+
+
+
+ Remote Proxy
+
+ Active
+
+
+
+
+
+
+
+ {remoteDisplayHost}
+
+
+ Traffic auto-routed to remote server
+
+
+
+ );
+ }
+
+ // Local mode: show original controls
+
return (
request<{ variants: Variant[] }>('/cliproxy'),
- getAuthStatus: () => request<{ authStatus: AuthStatus[] }>('/cliproxy/auth'),
+ getAuthStatus: () =>
+ request<{ authStatus: AuthStatus[]; source?: 'remote' | 'local' }>('/cliproxy/auth'),
create: (data: CreateVariant) =>
request('/cliproxy', {
method: 'POST',
@@ -307,16 +308,18 @@ export const api = {
// Multi-account management
accounts: {
- list: () => request<{ accounts: ProviderAccountsMap }>('/cliproxy/accounts'),
+ list: () => request<{ accounts: ProviderAccountsMap }>('/cliproxy/auth/accounts'),
listByProvider: (provider: string) =>
- request<{ provider: string; accounts: OAuthAccount[] }>(`/cliproxy/accounts/${provider}`),
+ request<{ provider: string; accounts: OAuthAccount[] }>(
+ `/cliproxy/auth/accounts/${provider}`
+ ),
setDefault: (provider: string, accountId: string) =>
- request(`/cliproxy/accounts/${provider}/default`, {
+ request(`/cliproxy/auth/accounts/${provider}/default`, {
method: 'POST',
body: JSON.stringify({ accountId }),
}),
remove: (provider: string, accountId: string) =>
- request(`/cliproxy/accounts/${provider}/${accountId}`, { method: 'DELETE' }),
+ request(`/cliproxy/auth/accounts/${provider}/${accountId}`, { method: 'DELETE' }),
},
// OAuth flow
auth: {
diff --git a/ui/src/pages/cliproxy.tsx b/ui/src/pages/cliproxy.tsx
index 46f668ac..b9fc21a9 100644
--- a/ui/src/pages/cliproxy.tsx
+++ b/ui/src/pages/cliproxy.tsx
@@ -192,6 +192,7 @@ export function CliproxyPage() {
} | null>(null);
const providers = useMemo(() => authData?.authStatus || [], [authData?.authStatus]);
+ const isRemoteMode = authData?.source === 'remote';
const variants = useMemo(() => variantsData?.variants || [], [variantsData?.variants]);
// Auto-select first provider if nothing selected
@@ -338,6 +339,7 @@ export function CliproxyPage() {
authStatus={parentAuthForVariant}
catalog={MODEL_CATALOGS[selectedVariantData.provider]}
logoProvider={selectedVariantData.provider}
+ isRemoteMode={isRemoteMode}
onAddAccount={() =>
setAddAccountProvider({
provider: selectedVariantData.provider,
@@ -365,6 +367,7 @@ export function CliproxyPage() {
displayName={selectedStatus.displayName}
authStatus={selectedStatus}
catalog={MODEL_CATALOGS[selectedStatus.provider]}
+ isRemoteMode={isRemoteMode}
onAddAccount={() =>
setAddAccountProvider({
provider: selectedStatus.provider,