mirror of
https://github.com/tiennm99/ccs.git
synced 2026-10-12 20:15:40 +00:00
feat(kiro): improve auth UX with normal browser default and URL display
- Change default to noIncognito=true (normal browser) for reliability - Always print OAuth URL to terminal for VS Code popup detection - Move incognito toggle from Proxy settings to Kiro provider page - Add --incognito flag to opt into incognito mode (was --no-incognito) - Update help text to reflect new defaults
This commit is contained in:
1 parent
083e67426c
commit
df0c94781e
9 files changed
+113
-79
No files matched your search
@@ -126,7 +126,7 @@ export async function triggerOAuth(
|
|||||||
options: OAuthOptions = {}
|
options: OAuthOptions = {}
|
||||||
): Promise<AccountInfo | null> {
|
): Promise<AccountInfo | null> {
|
||||||
const oauthConfig = getOAuthConfig(provider);
|
const oauthConfig = getOAuthConfig(provider);
|
||||||
const { verbose = false, add = false, nickname, fromUI = false, noIncognito = false } = options;
|
const { verbose = false, add = false, nickname, fromUI = false, noIncognito = true } = options;
|
||||||
const callbackPort = OAUTH_PORTS[provider];
|
const callbackPort = OAUTH_PORTS[provider];
|
||||||
const isCLI = !fromUI;
|
const isCLI = !fromUI;
|
||||||
const headless = options.headless ?? isHeadlessEnvironment();
|
const headless = options.headless ?? isHeadlessEnvironment();
|
||||||
|
|||||||
@@ -172,8 +172,8 @@ async function handleStdout(
|
|||||||
state.browserOpened = true;
|
state.browserOpened = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Display OAuth URLs in headless mode (for non-device-code flows)
|
// Display OAuth URL for all modes (enables VS Code terminal URL detection popup)
|
||||||
if (!isDeviceCodeFlow && options.headless && !state.urlDisplayed) {
|
if (!isDeviceCodeFlow && !state.urlDisplayed) {
|
||||||
const urlMatch = output.match(/https?:\/\/[^\s]+/);
|
const urlMatch = output.match(/https?:\/\/[^\s]+/);
|
||||||
if (urlMatch) {
|
if (urlMatch) {
|
||||||
console.log('');
|
console.log('');
|
||||||
|
|||||||
@@ -250,11 +250,16 @@ export async function execClaudeWithCLIProxy(
|
|||||||
const forceConfig = argsWithoutProxy.includes('--config');
|
const forceConfig = argsWithoutProxy.includes('--config');
|
||||||
const addAccount = argsWithoutProxy.includes('--add');
|
const addAccount = argsWithoutProxy.includes('--add');
|
||||||
const showAccounts = argsWithoutProxy.includes('--accounts');
|
const showAccounts = argsWithoutProxy.includes('--accounts');
|
||||||
// Kiro-specific: --no-incognito to use normal browser (saves login credentials)
|
// Kiro-specific: browser mode for OAuth
|
||||||
|
// Default to normal browser (noIncognito=true) for reliability - incognito often fails on Linux
|
||||||
|
// --incognito flag opts into incognito mode, --no-incognito is legacy (now default)
|
||||||
|
const incognitoFlag = argsWithoutProxy.includes('--incognito');
|
||||||
const noIncognitoFlag = argsWithoutProxy.includes('--no-incognito');
|
const noIncognitoFlag = argsWithoutProxy.includes('--no-incognito');
|
||||||
// Also check config.yaml for kiro_no_incognito setting
|
// Config setting (defaults to true = normal browser)
|
||||||
const kiroNoIncognitoConfig = provider === 'kiro' && unifiedConfig.cliproxy?.kiro_no_incognito;
|
const kiroNoIncognitoConfig =
|
||||||
const noIncognito = noIncognitoFlag || kiroNoIncognitoConfig;
|
provider === 'kiro' ? (unifiedConfig.cliproxy?.kiro_no_incognito ?? true) : false;
|
||||||
|
// --incognito flag overrides everything to use incognito
|
||||||
|
const noIncognito = incognitoFlag ? false : noIncognitoFlag || kiroNoIncognitoConfig;
|
||||||
|
|
||||||
// Parse --use <account> flag
|
// Parse --use <account> flag
|
||||||
let useAccount: string | undefined;
|
let useAccount: string | undefined;
|
||||||
@@ -597,6 +602,7 @@ export async function execClaudeWithCLIProxy(
|
|||||||
'--accounts',
|
'--accounts',
|
||||||
'--use',
|
'--use',
|
||||||
'--nickname',
|
'--nickname',
|
||||||
|
'--incognito',
|
||||||
'--no-incognito',
|
'--no-incognito',
|
||||||
// Proxy flags are handled by resolveProxyConfig, but list for documentation
|
// Proxy flags are handled by resolveProxyConfig, but list for documentation
|
||||||
...PROXY_CLI_FLAGS,
|
...PROXY_CLI_FLAGS,
|
||||||
|
|||||||
@@ -173,7 +173,7 @@ Run ${color('ccs config', 'command')} for web dashboard`.trim();
|
|||||||
['ccs <provider> --config', 'Change model (agy, gemini)'],
|
['ccs <provider> --config', 'Change model (agy, gemini)'],
|
||||||
['ccs <provider> --logout', 'Clear authentication'],
|
['ccs <provider> --logout', 'Clear authentication'],
|
||||||
['ccs <provider> --headless', 'Headless auth (for SSH)'],
|
['ccs <provider> --headless', 'Headless auth (for SSH)'],
|
||||||
['ccs kiro --no-incognito', 'Use normal browser (saves AWS login)'],
|
['ccs kiro --incognito', 'Use incognito browser (default: normal)'],
|
||||||
['ccs codex "explain code"', 'Use with prompt'],
|
['ccs codex "explain code"', 'Use with prompt'],
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import {
|
|||||||
} from '../../cliproxy/account-manager';
|
} from '../../cliproxy/account-manager';
|
||||||
import { getProxyTarget } from '../../cliproxy/proxy-target-resolver';
|
import { getProxyTarget } from '../../cliproxy/proxy-target-resolver';
|
||||||
import { fetchRemoteAuthStatus } from '../../cliproxy/remote-auth-fetcher';
|
import { fetchRemoteAuthStatus } from '../../cliproxy/remote-auth-fetcher';
|
||||||
|
import { loadOrCreateUnifiedConfig } from '../../config/unified-config-loader';
|
||||||
import type { CLIProxyProvider } from '../../cliproxy/types';
|
import type { CLIProxyProvider } from '../../cliproxy/types';
|
||||||
|
|
||||||
const router = Router();
|
const router = Router();
|
||||||
@@ -268,7 +269,7 @@ router.delete('/accounts/:provider/:accountId', (req: Request, res: Response): v
|
|||||||
*/
|
*/
|
||||||
router.post('/:provider/start', async (req: Request, res: Response): Promise<void> => {
|
router.post('/:provider/start', async (req: Request, res: Response): Promise<void> => {
|
||||||
const { provider } = req.params;
|
const { provider } = req.params;
|
||||||
const { nickname } = req.body;
|
const { nickname, noIncognito: noIncognitoBody } = req.body;
|
||||||
|
|
||||||
// Validate provider
|
// Validate provider
|
||||||
if (!validProviders.includes(provider as CLIProxyProvider)) {
|
if (!validProviders.includes(provider as CLIProxyProvider)) {
|
||||||
@@ -276,6 +277,14 @@ router.post('/:provider/start', async (req: Request, res: Response): Promise<voi
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check Kiro no-incognito setting from config (or request body)
|
||||||
|
// Default to true (use normal browser) for reliability - incognito often fails
|
||||||
|
let noIncognito = true;
|
||||||
|
if (provider === 'kiro') {
|
||||||
|
const config = loadOrCreateUnifiedConfig();
|
||||||
|
noIncognito = noIncognitoBody ?? config.cliproxy?.kiro_no_incognito ?? true;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Trigger OAuth flow - this opens browser and waits for completion
|
// Trigger OAuth flow - this opens browser and waits for completion
|
||||||
const account = await triggerOAuth(provider as CLIProxyProvider, {
|
const account = await triggerOAuth(provider as CLIProxyProvider, {
|
||||||
@@ -283,6 +292,7 @@ router.post('/:provider/start', async (req: Request, res: Response): Promise<voi
|
|||||||
headless: false, // Force interactive mode
|
headless: false, // Force interactive mode
|
||||||
nickname: nickname || undefined,
|
nickname: nickname || undefined,
|
||||||
fromUI: true, // Enable project selection prompt in UI
|
fromUI: true, // Enable project selection prompt in UI
|
||||||
|
noIncognito, // Kiro: use normal browser if enabled
|
||||||
});
|
});
|
||||||
|
|
||||||
if (account) {
|
if (account) {
|
||||||
|
|||||||
@@ -5,7 +5,8 @@
|
|||||||
|
|
||||||
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 { User, Plus } from 'lucide-react';
|
import { Switch } from '@/components/ui/switch';
|
||||||
|
import { User, Plus, Globe } from 'lucide-react';
|
||||||
import { AccountItem } from './account-item';
|
import { AccountItem } from './account-item';
|
||||||
import type { OAuthAccount } from '@/lib/api-client';
|
import type { OAuthAccount } from '@/lib/api-client';
|
||||||
|
|
||||||
@@ -16,6 +17,11 @@ interface AccountsSectionProps {
|
|||||||
onRemoveAccount: (accountId: string) => void;
|
onRemoveAccount: (accountId: string) => void;
|
||||||
isRemovingAccount?: boolean;
|
isRemovingAccount?: boolean;
|
||||||
privacyMode?: boolean;
|
privacyMode?: boolean;
|
||||||
|
/** Kiro-specific: show "use normal browser" toggle */
|
||||||
|
isKiro?: boolean;
|
||||||
|
kiroNoIncognito?: boolean;
|
||||||
|
onKiroNoIncognitoChange?: (enabled: boolean) => void;
|
||||||
|
kiroSettingsLoading?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function AccountsSection({
|
export function AccountsSection({
|
||||||
@@ -25,6 +31,10 @@ export function AccountsSection({
|
|||||||
onRemoveAccount,
|
onRemoveAccount,
|
||||||
isRemovingAccount,
|
isRemovingAccount,
|
||||||
privacyMode,
|
privacyMode,
|
||||||
|
isKiro,
|
||||||
|
kiroNoIncognito,
|
||||||
|
onKiroNoIncognitoChange,
|
||||||
|
kiroSettingsLoading,
|
||||||
}: AccountsSectionProps) {
|
}: AccountsSectionProps) {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
@@ -64,6 +74,24 @@ export function AccountsSection({
|
|||||||
<p className="text-xs opacity-70">Add an account to get started</p>
|
<p className="text-xs opacity-70">Add an account to get started</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{/* Kiro-specific: Incognito browser setting - users complain "it keeps opening incognito" */}
|
||||||
|
{isKiro && onKiroNoIncognitoChange && (
|
||||||
|
<div className="mt-3 pt-3 border-t border-dashed">
|
||||||
|
<div className="flex items-center justify-between gap-3">
|
||||||
|
<div className="flex items-center gap-2 text-sm text-muted-foreground">
|
||||||
|
<Globe className="w-3.5 h-3.5" />
|
||||||
|
<span>Use incognito</span>
|
||||||
|
</div>
|
||||||
|
<Switch
|
||||||
|
checked={!kiroNoIncognito}
|
||||||
|
onCheckedChange={(v) => onKiroNoIncognitoChange(!v)}
|
||||||
|
disabled={kiroSettingsLoading}
|
||||||
|
className="scale-90"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -157,6 +157,7 @@ export function ProviderEditor({
|
|||||||
className="flex-1 mt-0 border-0 p-0 data-[state=inactive]:hidden flex flex-col overflow-hidden"
|
className="flex-1 mt-0 border-0 p-0 data-[state=inactive]:hidden flex flex-col overflow-hidden"
|
||||||
>
|
>
|
||||||
<ModelConfigTab
|
<ModelConfigTab
|
||||||
|
provider={provider}
|
||||||
catalog={catalog}
|
catalog={catalog}
|
||||||
savedPresets={savedPresets}
|
savedPresets={savedPresets}
|
||||||
currentModel={currentModel}
|
currentModel={currentModel}
|
||||||
|
|||||||
@@ -1,16 +1,19 @@
|
|||||||
/**
|
/**
|
||||||
* Model Config Tab
|
* Model Config Tab
|
||||||
* Contains model config section and accounts section
|
* Contains model config section, accounts section, and provider-specific settings
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { useCallback, useEffect, useState } from 'react';
|
||||||
import { ScrollArea } from '@/components/ui/scroll-area';
|
import { ScrollArea } from '@/components/ui/scroll-area';
|
||||||
import { Separator } from '@/components/ui/separator';
|
import { Separator } from '@/components/ui/separator';
|
||||||
import { ModelConfigSection } from './model-config-section';
|
import { ModelConfigSection } from './model-config-section';
|
||||||
import { AccountsSection } from './accounts-section';
|
import { AccountsSection } from './accounts-section';
|
||||||
|
import { api } from '@/lib/api-client';
|
||||||
import type { ProviderCatalog } from '../provider-model-selector';
|
import type { ProviderCatalog } from '../provider-model-selector';
|
||||||
import type { OAuthAccount } from '@/lib/api-client';
|
import type { OAuthAccount } from '@/lib/api-client';
|
||||||
|
|
||||||
interface ModelConfigTabProps {
|
interface ModelConfigTabProps {
|
||||||
|
provider: string;
|
||||||
catalog?: ProviderCatalog;
|
catalog?: ProviderCatalog;
|
||||||
savedPresets: Array<{
|
savedPresets: Array<{
|
||||||
name: string;
|
name: string;
|
||||||
@@ -38,6 +41,7 @@ interface ModelConfigTabProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function ModelConfigTab({
|
export function ModelConfigTab({
|
||||||
|
provider,
|
||||||
catalog,
|
catalog,
|
||||||
savedPresets,
|
savedPresets,
|
||||||
currentModel,
|
currentModel,
|
||||||
@@ -57,6 +61,53 @@ export function ModelConfigTab({
|
|||||||
isRemovingAccount,
|
isRemovingAccount,
|
||||||
privacyMode,
|
privacyMode,
|
||||||
}: ModelConfigTabProps) {
|
}: ModelConfigTabProps) {
|
||||||
|
// Kiro-specific: no-incognito setting (defaults to true = normal browser)
|
||||||
|
const isKiro = provider === 'kiro';
|
||||||
|
const [kiroNoIncognito, setKiroNoIncognito] = useState(true);
|
||||||
|
const [kiroSettingsLoading, setKiroSettingsLoading] = useState(true);
|
||||||
|
const [kiroSaving, setKiroSaving] = useState(false);
|
||||||
|
|
||||||
|
// Fetch Kiro settings from unified config
|
||||||
|
const fetchKiroSettings = useCallback(async () => {
|
||||||
|
if (!isKiro) return;
|
||||||
|
try {
|
||||||
|
setKiroSettingsLoading(true);
|
||||||
|
const unifiedConfig = await api.config.get();
|
||||||
|
const cliproxyConfig = unifiedConfig.cliproxy as { kiro_no_incognito?: boolean } | undefined;
|
||||||
|
setKiroNoIncognito(cliproxyConfig?.kiro_no_incognito ?? true);
|
||||||
|
} catch {
|
||||||
|
setKiroNoIncognito(true);
|
||||||
|
} finally {
|
||||||
|
setKiroSettingsLoading(false);
|
||||||
|
}
|
||||||
|
}, [isKiro]);
|
||||||
|
|
||||||
|
// Save Kiro no-incognito setting
|
||||||
|
const saveKiroNoIncognito = useCallback(async (enabled: boolean) => {
|
||||||
|
setKiroNoIncognito(enabled); // Optimistic update
|
||||||
|
setKiroSaving(true);
|
||||||
|
try {
|
||||||
|
const unifiedConfig = await api.config.get();
|
||||||
|
const existingCliproxy = (unifiedConfig.cliproxy ?? {}) as Record<string, unknown>;
|
||||||
|
await api.config.update({
|
||||||
|
...unifiedConfig,
|
||||||
|
cliproxy: {
|
||||||
|
...existingCliproxy,
|
||||||
|
kiro_no_incognito: enabled,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} catch {
|
||||||
|
setKiroNoIncognito(!enabled); // Revert on error
|
||||||
|
} finally {
|
||||||
|
setKiroSaving(false);
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
// Load Kiro settings on mount
|
||||||
|
useEffect(() => {
|
||||||
|
fetchKiroSettings();
|
||||||
|
}, [fetchKiroSettings]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ScrollArea className="flex-1">
|
<ScrollArea className="flex-1">
|
||||||
<div className="p-4 space-y-6">
|
<div className="p-4 space-y-6">
|
||||||
@@ -82,6 +133,10 @@ export function ModelConfigTab({
|
|||||||
onRemoveAccount={onRemoveAccount}
|
onRemoveAccount={onRemoveAccount}
|
||||||
isRemovingAccount={isRemovingAccount}
|
isRemovingAccount={isRemovingAccount}
|
||||||
privacyMode={privacyMode}
|
privacyMode={privacyMode}
|
||||||
|
isKiro={isKiro}
|
||||||
|
kiroNoIncognito={kiroNoIncognito}
|
||||||
|
onKiroNoIncognitoChange={saveKiroNoIncognito}
|
||||||
|
kiroSettingsLoading={kiroSettingsLoading || kiroSaving}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</ScrollArea>
|
</ScrollArea>
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
* Settings section for CLIProxyAPI configuration (local/remote)
|
* Settings section for CLIProxyAPI configuration (local/remote)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { useEffect, useState, useCallback } from 'react';
|
import { useEffect } from 'react';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { Alert, AlertDescription } from '@/components/ui/alert';
|
import { Alert, AlertDescription } from '@/components/ui/alert';
|
||||||
import { ScrollArea } from '@/components/ui/scroll-area';
|
import { ScrollArea } from '@/components/ui/scroll-area';
|
||||||
@@ -12,7 +12,6 @@ import { RefreshCw, CheckCircle2, AlertCircle, Laptop, Cloud } from 'lucide-reac
|
|||||||
import { useProxyConfig, useRawConfig } from '../../hooks';
|
import { useProxyConfig, useRawConfig } from '../../hooks';
|
||||||
import { LocalProxyCard } from './local-proxy-card';
|
import { LocalProxyCard } from './local-proxy-card';
|
||||||
import { RemoteProxyCard } from './remote-proxy-card';
|
import { RemoteProxyCard } from './remote-proxy-card';
|
||||||
import { api } from '@/lib/api-client';
|
|
||||||
|
|
||||||
export default function ProxySection() {
|
export default function ProxySection() {
|
||||||
const {
|
const {
|
||||||
@@ -38,54 +37,11 @@ export default function ProxySection() {
|
|||||||
|
|
||||||
const { fetchRawConfig } = useRawConfig();
|
const { fetchRawConfig } = useRawConfig();
|
||||||
|
|
||||||
// Kiro provider settings state
|
|
||||||
const [kiroNoIncognito, setKiroNoIncognito] = useState(false);
|
|
||||||
const [kiroSettingsLoading, setKiroSettingsLoading] = useState(true);
|
|
||||||
const [kiroSaving, setKiroSaving] = useState(false);
|
|
||||||
|
|
||||||
// Fetch Kiro settings from unified config
|
|
||||||
const fetchKiroSettings = useCallback(async () => {
|
|
||||||
try {
|
|
||||||
setKiroSettingsLoading(true);
|
|
||||||
const unifiedConfig = await api.config.get();
|
|
||||||
const cliproxyConfig = unifiedConfig.cliproxy as { kiro_no_incognito?: boolean } | undefined;
|
|
||||||
setKiroNoIncognito(cliproxyConfig?.kiro_no_incognito ?? false);
|
|
||||||
} catch {
|
|
||||||
// Config may not exist yet, use default
|
|
||||||
setKiroNoIncognito(false);
|
|
||||||
} finally {
|
|
||||||
setKiroSettingsLoading(false);
|
|
||||||
}
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
// Save Kiro no-incognito setting
|
|
||||||
const saveKiroNoIncognito = useCallback(async (enabled: boolean) => {
|
|
||||||
setKiroNoIncognito(enabled); // Optimistic update
|
|
||||||
setKiroSaving(true);
|
|
||||||
try {
|
|
||||||
const unifiedConfig = await api.config.get();
|
|
||||||
const existingCliproxy = (unifiedConfig.cliproxy ?? {}) as Record<string, unknown>;
|
|
||||||
await api.config.update({
|
|
||||||
...unifiedConfig,
|
|
||||||
cliproxy: {
|
|
||||||
...existingCliproxy,
|
|
||||||
kiro_no_incognito: enabled,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
} catch {
|
|
||||||
// Revert on error
|
|
||||||
setKiroNoIncognito(!enabled);
|
|
||||||
} finally {
|
|
||||||
setKiroSaving(false);
|
|
||||||
}
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
// Load data on mount
|
// Load data on mount
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchConfig();
|
fetchConfig();
|
||||||
fetchRawConfig();
|
fetchRawConfig();
|
||||||
fetchKiroSettings();
|
}, [fetchConfig, fetchRawConfig]);
|
||||||
}, [fetchConfig, fetchRawConfig, fetchKiroSettings]);
|
|
||||||
|
|
||||||
if (loading || !config) {
|
if (loading || !config) {
|
||||||
return (
|
return (
|
||||||
@@ -309,27 +265,6 @@ export default function ProxySection() {
|
|||||||
onSaveConfig={saveConfig}
|
onSaveConfig={saveConfig}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Provider Settings */}
|
|
||||||
<div className="space-y-3">
|
|
||||||
<h3 className="text-base font-medium">Provider Settings</h3>
|
|
||||||
<div className="space-y-3 p-4 rounded-lg border bg-muted/30">
|
|
||||||
{/* Kiro: Use normal browser */}
|
|
||||||
<div className="flex items-center justify-between">
|
|
||||||
<div>
|
|
||||||
<p className="font-medium text-sm">Kiro: Use normal browser</p>
|
|
||||||
<p className="text-xs text-muted-foreground">
|
|
||||||
Save AWS login credentials (disable incognito mode)
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<Switch
|
|
||||||
checked={kiroNoIncognito}
|
|
||||||
onCheckedChange={saveKiroNoIncognito}
|
|
||||||
disabled={kiroSaving || kiroSettingsLoading}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</ScrollArea>
|
</ScrollArea>
|
||||||
|
|
||||||
@@ -341,7 +276,6 @@ export default function ProxySection() {
|
|||||||
onClick={() => {
|
onClick={() => {
|
||||||
fetchConfig();
|
fetchConfig();
|
||||||
fetchRawConfig();
|
fetchRawConfig();
|
||||||
fetchKiroSettings();
|
|
||||||
}}
|
}}
|
||||||
disabled={loading || saving}
|
disabled={loading || saving}
|
||||||
className="w-full"
|
className="w-full"
|
||||||
|
|||||||
Reference in new issue
Block a user