import { ChatBubbleLeftRightIcon, CogIcon, CpuChipIcon, DocumentTextIcon, ServerIcon, ShieldCheckIcon, WrenchScrewdriverIcon, } from '@heroicons/react/24/outline'; import { useMemo, useState } from 'react'; import SettingsHeader from '../components/SettingsHeader'; import { useSettingsNavigation } from '../hooks/useSettingsNavigation'; import SectionCard from './components/SectionCard'; import InputGroup, { Field, CheckboxField } from './components/InputGroup'; import ValidatedField, { ValidatedSelect } from './components/ValidatedField'; import ActionPanel, { PrimaryButton } from './components/ActionPanel'; import DaemonHealthIndicator from '../../daemon/DaemonHealthIndicator'; import { useDaemonHealth, formatRelativeTime } from '../../../hooks/useDaemonHealth'; import { alphahumanAgentChat, alphahumanDecryptSecret, alphahumanDoctorModels, alphahumanDoctorReport, alphahumanEncryptSecret, alphahumanGetConfig, alphahumanGetIntegrationInfo, alphahumanHardwareDiscover, alphahumanHardwareIntrospect, alphahumanListIntegrations, alphahumanMigrateOpenclaw, alphahumanModelsRefresh, alphahumanServiceInstall, alphahumanServiceStatus, alphahumanServiceUninstall, alphahumanUpdateGatewaySettings, alphahumanUpdateMemorySettings, alphahumanUpdateModelSettings, alphahumanUpdateRuntimeSettings, alphahumanUpdateTunnelSettings, isTauri, runtimeDisableSkill, runtimeEnableSkill, runtimeIsSkillEnabled, runtimeListSkills, SkillSnapshot, TunnelConfig, } from '../../../utils/tauriCommands'; import SettingsHeader from '../components/SettingsHeader'; import { useSettingsNavigation } from '../hooks/useSettingsNavigation'; import ActionPanel, { PrimaryButton } from './components/ActionPanel'; import InputGroup, { CheckboxField, Field } from './components/InputGroup'; import SectionCard from './components/SectionCard'; const formatJson = (value: unknown) => JSON.stringify(value, null, 2); const TauriCommandsPanel = () => { const { navigateBack } = useSettingsNavigation(); const daemonHealth = useDaemonHealth(); // View mode removed - always show all sections const [expandedSections] = useState>( new Set([ 'system-configuration', 'runtime-execution', 'security-data', 'network-infrastructure', 'development-operations', 'interactive-tools', ]) ); // Output and error states const [output, setOutput] = useState(''); const [error, setError] = useState(''); // Form states (preserved from original) const [providerOverride, setProviderOverride] = useState(''); const [integrationName, setIntegrationName] = useState(''); const [hardwarePath, setHardwarePath] = useState(''); const [migrationSource, setMigrationSource] = useState(''); const [encryptInput, setEncryptInput] = useState(''); const [decryptInput, setDecryptInput] = useState(''); const [apiKey, setApiKey] = useState(''); const [apiUrl, setApiUrl] = useState(''); const [defaultProvider, setDefaultProvider] = useState(''); const [defaultModel, setDefaultModel] = useState(''); const [defaultTemp, setDefaultTemp] = useState('0.7'); const [tunnelProvider, setTunnelProvider] = useState('none'); const [cloudflareToken, setCloudflareToken] = useState(''); const [ngrokToken, setNgrokToken] = useState(''); const [tailscaleHostname, setTailscaleHostname] = useState(''); const [customCommand, setCustomCommand] = useState(''); const [gatewayHost, setGatewayHost] = useState('127.0.0.1'); const [gatewayPort, setGatewayPort] = useState('3000'); const [gatewayPairing, setGatewayPairing] = useState(true); const [gatewayPublic, setGatewayPublic] = useState(false); const [memoryBackend, setMemoryBackend] = useState('sqlite'); const [memoryAutoSave, setMemoryAutoSave] = useState(true); const [embeddingProvider, setEmbeddingProvider] = useState('none'); const [embeddingModel, setEmbeddingModel] = useState('text-embedding-3-small'); const [embeddingDims, setEmbeddingDims] = useState('1536'); const [runtimeKind, setRuntimeKind] = useState('native'); const [reasoningEnabled, setReasoningEnabled] = useState(false); const [skills, setSkills] = useState>([]); const [skillsLoading, setSkillsLoading] = useState(false); const [chatInput, setChatInput] = useState(''); const [chatProvider, setChatProvider] = useState(''); const [chatModel, setChatModel] = useState(''); const [chatTemperature, setChatTemperature] = useState('0.7'); const [chatLog, setChatLog] = useState>([]); // Loading states const [operationLoading, setOperationLoading] = useState(''); // Enhanced System Configuration state management const [hasUnsavedChanges, setHasUnsavedChanges] = useState(false); const [originalConfig, setOriginalConfig] = useState>({}); const [fieldErrors, setFieldErrors] = useState>({}); const [lastSaveTime, setLastSaveTime] = useState(null); const [validationLoading, setValidationLoading] = useState(false); const [configLoaded, setConfigLoaded] = useState(false); const tauriAvailable = useMemo(() => isTauri(), []); const parseOptionalNumber = (value: string): number | null => { if (!value.trim()) { return null; } const parsed = Number(value); return Number.isFinite(parsed) ? parsed : null; }; // Provider configurations for smart defaults const providerConfigs = useMemo(() => ({ openai: { defaultUrl: 'https://api.openai.com/v1', keyPattern: /^sk-[a-zA-Z0-9]{32,}$/, models: ['gpt-4', 'gpt-4-turbo', 'gpt-4o', 'gpt-3.5-turbo'] }, anthropic: { defaultUrl: 'https://api.anthropic.com', keyPattern: /^sk-ant-[a-zA-Z0-9_-]{32,}$/, models: ['claude-sonnet-4-5-20250929', 'claude-opus-4-6', 'claude-haiku-3-5'] }, ollama: { defaultUrl: 'http://localhost:11434', keyPattern: null, // No API key required models: ['llama3', 'llama3:8b', 'codellama', 'mistral', 'phi3'] }, groq: { defaultUrl: 'https://api.groq.com/openai/v1', keyPattern: /^gsk_[a-zA-Z0-9]{32,}$/, models: ['llama-3.1-70b-versatile', 'llama-3.1-8b-instant', 'mixtral-8x7b-32768'] }, cohere: { defaultUrl: 'https://api.cohere.ai/v1', keyPattern: /^[a-zA-Z0-9]{32,}$/, models: ['command-r', 'command-r-plus', 'command-light'] } }), []); // Validation functions const validateApiKey = useCallback((key: string, provider: string): string | null => { if (!provider || provider === 'none') return null; if (!key.trim() && provider && provider !== 'none' && provider !== 'ollama') { return 'API key is required for this provider'; } if (key.trim() && provider && providerConfigs[provider as keyof typeof providerConfigs]?.keyPattern) { const pattern = providerConfigs[provider as keyof typeof providerConfigs].keyPattern; if (pattern && !pattern.test(key)) { return `Invalid API key format for ${provider}`; } } return null; }, [providerConfigs]); const validateApiUrl = useCallback((url: string): string | null => { if (!url.trim()) return null; try { const parsedUrl = new URL(url); if (!['http:', 'https:'].includes(parsedUrl.protocol)) { return 'URL must use HTTP or HTTPS protocol'; } if (parsedUrl.protocol === 'http:' && !parsedUrl.hostname.includes('localhost') && !parsedUrl.hostname.includes('127.0.0.1')) { return 'HTTP URLs are only allowed for localhost'; } return null; } catch { return 'Invalid URL format'; } }, []); const validateProvider = useCallback((provider: string): string | null => { if (!provider.trim()) return null; const supportedProviders = Object.keys(providerConfigs); if (!supportedProviders.includes(provider)) { return `Unsupported provider. Supported: ${supportedProviders.join(', ')}`; } return null; }, [providerConfigs]); const validateModel = useCallback((model: string, provider: string): string | null => { if (!model.trim() || !provider.trim()) return null; const config = providerConfigs[provider as keyof typeof providerConfigs]; if (config && !config.models.includes(model)) { return `Model not available for ${provider}. Try: ${config.models.slice(0, 3).join(', ')}`; } return null; }, [providerConfigs]); const validateTemperature = useCallback((temp: string): string | null => { if (!temp.trim()) return null; const value = parseFloat(temp); if (isNaN(value)) return 'Temperature must be a number'; if (value < 0 || value > 2) return 'Temperature must be between 0.0 and 2.0'; return null; }, []); // Real-time validation const performValidation = useCallback(() => { const errors: Record = {}; const apiKeyError = validateApiKey(apiKey, defaultProvider); if (apiKeyError) errors.apiKey = apiKeyError; const apiUrlError = validateApiUrl(apiUrl); if (apiUrlError) errors.apiUrl = apiUrlError; const providerError = validateProvider(defaultProvider); if (providerError) errors.defaultProvider = providerError; const modelError = validateModel(defaultModel, defaultProvider); if (modelError) errors.defaultModel = modelError; const tempError = validateTemperature(defaultTemp); if (tempError) errors.defaultTemp = tempError; setFieldErrors(errors); return Object.keys(errors).length === 0; }, [apiKey, apiUrl, defaultProvider, defaultModel, defaultTemp, validateApiKey, validateApiUrl, validateProvider, validateModel, validateTemperature]); // Format timestamp for display const formatTime = useCallback((date: Date): string => { return date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', second: '2-digit' }); }, []); // Track changes useEffect(() => { if (!configLoaded) return; const currentConfig = { api_key: apiKey, api_url: apiUrl, default_provider: defaultProvider, default_model: defaultModel, default_temperature: defaultTemp, }; const hasChanges = JSON.stringify(currentConfig) !== JSON.stringify(originalConfig); setHasUnsavedChanges(hasChanges); // Perform validation on changes performValidation(); }, [apiKey, apiUrl, defaultProvider, defaultModel, defaultTemp, originalConfig, configLoaded, performValidation]); // Auto-populate URL based on provider useEffect(() => { if (defaultProvider && !apiUrl && providerConfigs[defaultProvider as keyof typeof providerConfigs]) { const config = providerConfigs[defaultProvider as keyof typeof providerConfigs]; setApiUrl(config.defaultUrl); } }, [defaultProvider, apiUrl, providerConfigs]); const run = async (fn: () => Promise, operationName?: string) => { setError(''); if (operationName) setOperationLoading(operationName); try { const result = await fn(); setOutput(formatJson(result)); } catch (err) { const message = err instanceof Error ? err.message : String(err); setError(message); } finally { setOperationLoading(''); } }; const runWithResult = async ( fn: () => Promise, operationName?: string ): Promise => { setError(''); if (operationName) setOperationLoading(operationName); try { const result = await fn(); setOutput(formatJson(result)); return result; } catch (err) { const message = err instanceof Error ? err.message : String(err); setError(message); return null; } finally { setOperationLoading(''); } }; const loadConfig = async () => { const response = await runWithResult(() => alphahumanGetConfig(), 'loadConfig'); if (!response) { setError('Failed to load configuration'); return; } try { const snapshot = response.result; const config = snapshot.config as Record; // Extract model configuration const modelApiKey = (config.api_key as string) ?? ''; const modelApiUrl = (config.api_url as string) ?? ''; const modelProvider = (config.default_provider as string) ?? ''; const modelModel = (config.default_model as string) ?? ''; const modelTemp = String((config.default_temperature as number) ?? 0.7); // Set state setApiKey(modelApiKey); setApiUrl(modelApiUrl); setDefaultProvider(modelProvider); setDefaultModel(modelModel); setDefaultTemp(modelTemp); // Store original config for change tracking const systemConfig = { api_key: modelApiKey, api_url: modelApiUrl, default_provider: modelProvider, default_model: modelModel, default_temperature: modelTemp, }; setOriginalConfig(systemConfig); setConfigLoaded(true); // Load other configuration sections const tunnel = (config.tunnel as Record) ?? {}; setTunnelProvider((tunnel.provider as string) ?? 'none'); setCloudflareToken(((tunnel.cloudflare as Record)?.token as string) ?? ''); setNgrokToken(((tunnel.ngrok as Record)?.auth_token as string) ?? ''); setTailscaleHostname( ((tunnel.tailscale as Record)?.hostname as string) ?? '' ); setCustomCommand(((tunnel.custom as Record)?.start_command as string) ?? ''); const gateway = (config.gateway as Record) ?? {}; setGatewayHost((gateway.host as string) ?? '127.0.0.1'); setGatewayPort(String((gateway.port as number) ?? 3000)); setGatewayPairing((gateway.require_pairing as boolean) ?? true); setGatewayPublic((gateway.allow_public_bind as boolean) ?? false); const memory = (config.memory as Record) ?? {}; setMemoryBackend((memory.backend as string) ?? 'sqlite'); setMemoryAutoSave((memory.auto_save as boolean) ?? true); setEmbeddingProvider((memory.embedding_provider as string) ?? 'none'); setEmbeddingModel((memory.embedding_model as string) ?? 'text-embedding-3-small'); setEmbeddingDims(String((memory.embedding_dimensions as number) ?? 1536)); const runtime = (config.runtime as Record) ?? {}; setRuntimeKind((runtime.kind as string) ?? 'native'); setReasoningEnabled((runtime.reasoning_enabled as boolean) ?? false); // Clear any previous errors setFieldErrors({}); } catch (err) { const message = err instanceof Error ? err.message : String(err); setError(`Failed to parse configuration: ${message}`); } }; const buildTunnelConfig = (): TunnelConfig => { if (tunnelProvider === 'cloudflare') { return { provider: 'cloudflare', cloudflare: { token: cloudflareToken } }; } if (tunnelProvider === 'ngrok') { return { provider: 'ngrok', ngrok: { auth_token: ngrokToken } }; } if (tunnelProvider === 'tailscale') { return { provider: 'tailscale', tailscale: { hostname: tailscaleHostname || null } }; } if (tunnelProvider === 'custom') { return { provider: 'custom', custom: { start_command: customCommand } }; } return { provider: 'none' }; }; const saveModelSettings = async () => { // Pre-save validation if (!performValidation()) { setError('Please fix validation errors before saving'); return; } setError(''); setOperationLoading('saveModelSettings'); try { const result = await alphahumanUpdateModelSettings({ api_key: apiKey.trim() ? apiKey : null, api_url: apiUrl.trim() ? apiUrl : null, default_provider: defaultProvider.trim() ? defaultProvider : null, default_model: defaultModel.trim() ? defaultModel : null, default_temperature: parseOptionalNumber(defaultTemp), }); setOutput(formatJson(result)); // Success feedback const now = new Date(); setLastSaveTime(now); setHasUnsavedChanges(false); // Update original config setOriginalConfig({ api_key: apiKey, api_url: apiUrl, default_provider: defaultProvider, default_model: defaultModel, default_temperature: defaultTemp, }); } catch (err) { const message = err instanceof Error ? err.message : String(err); if (message.includes('API key')) { setFieldErrors(prev => ({ ...prev, apiKey: 'Invalid API key or authentication failed' })); } else if (message.includes('provider')) { setFieldErrors(prev => ({ ...prev, defaultProvider: 'Provider not supported or misconfigured' })); } else if (message.includes('model')) { setFieldErrors(prev => ({ ...prev, defaultModel: 'Model not available for this provider' })); } setError(message); } finally { setOperationLoading(''); } }; const testConnection = async () => { if (!performValidation()) { setError('Please fix validation errors before testing connection'); return; } if (!defaultProvider || (!apiKey && defaultProvider !== 'ollama')) { setError('Provider and API key are required to test connection (except for Ollama)'); return; } // Check if running in Tauri environment if (!isTauri()) { setError('Test Connection is only available in the desktop application'); return; } setValidationLoading(true); setError(''); try { // Add timeout to prevent infinite loading const timeoutPromise = new Promise((_, reject) => setTimeout(() => reject(new Error('Connection test timed out after 30 seconds')), 30000) ); // Test connection by attempting to refresh models with current settings const result = await Promise.race([ alphahumanModelsRefresh(defaultProvider, false), timeoutPromise ]); setOutput(formatJson(result)); // If we get here, connection is successful const successMessage = `Connection test successful for ${defaultProvider}`; setOutput(prev => prev + '\n\n' + successMessage); // Clear any previous connection errors setFieldErrors(prev => { const newErrors = { ...prev }; delete newErrors.apiKey; delete newErrors.defaultProvider; return newErrors; }); } catch (err) { const message = err instanceof Error ? err.message : String(err); console.error('Test connection error:', err); // Set provider-specific errors if (message.includes('authentication') || message.includes('401')) { setFieldErrors(prev => ({ ...prev, apiKey: 'Authentication failed - check API key' })); } else if (message.includes('provider') || message.includes('404')) { setFieldErrors(prev => ({ ...prev, defaultProvider: 'Provider not found or unavailable' })); } else if (message.includes('network') || message.includes('timeout')) { setFieldErrors(prev => ({ ...prev, apiUrl: 'Network error - check API URL and connectivity' })); } else if (message.includes('Not running in Tauri')) { setFieldErrors(prev => ({ ...prev, defaultProvider: 'Desktop application required for testing' })); } setError(`Connection test failed: ${message}`); } finally { setValidationLoading(false); } }; const saveTunnelSettings = () => run(() => alphahumanUpdateTunnelSettings(buildTunnelConfig()), 'saveTunnelSettings'); const saveGatewaySettings = () => run( () => alphahumanUpdateGatewaySettings({ host: gatewayHost.trim() ? gatewayHost : null, port: parseOptionalNumber(gatewayPort), require_pairing: gatewayPairing, allow_public_bind: gatewayPublic, }), 'saveGatewaySettings' ); const saveMemorySettings = () => run( () => alphahumanUpdateMemorySettings({ backend: memoryBackend.trim() ? memoryBackend : null, auto_save: memoryAutoSave, embedding_provider: embeddingProvider.trim() ? embeddingProvider : null, embedding_model: embeddingModel.trim() ? embeddingModel : null, embedding_dimensions: parseOptionalNumber(embeddingDims), }), 'saveMemorySettings' ); const saveRuntimeSettings = () => run( () => alphahumanUpdateRuntimeSettings({ kind: runtimeKind.trim() ? runtimeKind : null, reasoning_enabled: reasoningEnabled, }), 'saveRuntimeSettings' ); const loadSkills = async () => { setSkillsLoading(true); try { const snapshots = await runtimeListSkills(); const enriched = await Promise.all( snapshots.map(async snapshot => ({ snapshot, enabled: await runtimeIsSkillEnabled(snapshot.skill_id), })) ); setSkills(enriched); setOutput(formatJson(enriched)); } catch (err) { const message = err instanceof Error ? err.message : String(err); setError(message); } finally { setSkillsLoading(false); } }; const toggleSkill = async (skillId: string, nextEnabled: boolean) => { if (nextEnabled) { await run(() => runtimeEnableSkill(skillId), 'enableSkill'); } else { await run(() => runtimeDisableSkill(skillId), 'disableSkill'); } setSkills(prev => prev.map(item => item.snapshot.skill_id === skillId ? { ...item, enabled: nextEnabled } : item ) ); }; const sendChat = async () => { if (!chatInput.trim()) { return; } const userMessage = chatInput.trim(); setChatLog(prev => [...prev, { role: 'user', text: userMessage }]); setChatInput(''); const response = await runWithResult( () => alphahumanAgentChat( userMessage, chatProvider.trim() ? chatProvider : undefined, chatModel.trim() ? chatModel : undefined, parseOptionalNumber(chatTemperature) ?? undefined ), 'sendChat' ); if (response) { setChatLog(prev => [...prev, { role: 'agent', text: response.result }]); } }; // Always show all sections const isSectionVisible = () => { return true; // Always show all sections }; const isCollapsed = (sectionId: string) => { return !expandedSections.has(sectionId); }; // Helper to check if a section is collapsed (currently unused but kept for future expansion) // const toggleSection = (sectionId: string) => { // const newExpanded = new Set(expandedSections); // if (newExpanded.has(sectionId)) { // newExpanded.delete(sectionId); // } else { // newExpanded.add(sectionId); // } // setExpandedSections(newExpanded); // }; return (
{!tauriAvailable && (
Tauri runtime not detected. Commands will fail in browser mode.
)} {operationLoading && (
{operationLoading}
)} {/* Critical Path - Always visible in grid on desktop */}
{/* Category 1: System Configuration */} {isSectionVisible() && ( } collapsible={true} defaultExpanded={!isCollapsed('system-configuration')} hasChanges={hasUnsavedChanges} loading={operationLoading === 'loadConfig' || operationLoading === 'saveModelSettings' || validationLoading} > { setDefaultProvider(value); // Auto-populate URL when provider changes if (value && providerConfigs[value as keyof typeof providerConfigs]) { const config = providerConfigs[value as keyof typeof providerConfigs]; if (!apiUrl || Object.values(providerConfigs).some(c => c.defaultUrl === apiUrl)) { setApiUrl(config.defaultUrl); } } }} options={[ { value: '', label: 'Select provider...', description: 'Choose your AI service provider' }, { value: 'openai', label: 'OpenAI', description: 'GPT models with high performance' }, { value: 'anthropic', label: 'Anthropic', description: 'Claude models with safety focus' }, { value: 'ollama', label: 'Ollama', description: 'Local models, no API key needed' }, { value: 'groq', label: 'Groq', description: 'Fast inference with LPU acceleration' }, { value: 'cohere', label: 'Cohere', description: 'Enterprise-grade language models' }, ]} error={fieldErrors.defaultProvider} required={true} helpText="Primary AI provider for agent operations. Each provider offers different models with unique capabilities and pricing." /> ({ value: model, label: model, description: model.includes('gpt-4') ? 'Advanced reasoning, higher cost' : model.includes('gpt-3.5') ? 'Fast and economical' : model.includes('claude') ? 'Excellent analysis and safety' : model.includes('llama') ? 'Open source, good performance' : model.includes('mixtral') ? 'Mixture of experts model' : 'High-quality language model' })) || []) ]} error={fieldErrors.defaultModel} helpText="Primary language model for agent interactions. Available models are filtered based on your selected provider." disabled={!defaultProvider} validation={ !defaultModel ? 'none' : fieldErrors.defaultModel ? 'invalid' : defaultProvider && validateModel(defaultModel, defaultProvider) === null ? 'valid' : 'none' } /> Load Config Test Connection 0 || !hasUnsavedChanges} > Save Settings )} {/* Category 2: Runtime & Execution */} {isSectionVisible() && ( } collapsible={true} defaultExpanded={!isCollapsed('runtime-execution')} hasChanges={false} loading={operationLoading === 'saveRuntimeSettings' || skillsLoading}> setRuntimeKind(event.target.value)} /> Save Runtime Settings {skillsLoading ? 'Loading Skills…' : 'Load Skills'} {skills.length > 0 && (
Skills
{skills.map(item => (
{item.snapshot.name}
{item.snapshot.skill_id}
toggleSkill(item.snapshot.skill_id, checked)} className="text-xs ml-4 flex-shrink-0" />
))}
)}
{/* Live Status Display */}
Daemon Status: {daemonHealth.status}
Last update: {daemonHealth.lastUpdate ? formatRelativeTime(daemonHealth.lastUpdate) : 'Never'}
{daemonHealth.healthSnapshot && (
PID: {daemonHealth.healthSnapshot.pid} • Uptime: {daemonHealth.uptimeText}
)}
{daemonHealth.status === 'error' && ( daemonHealth.restartDaemon()} variant="outline" loading={daemonHealth.isRecovering} > Restart )}
{/* Component Health */} {daemonHealth.componentCount > 0 && (
{Object.entries(daemonHealth.components).map(([name, health]) => (
{name} {health.restart_count > 0 && ( ({health.restart_count}) )}
))}
)} {/* Service Controls */} daemonHealth.startDaemon()} loading={operationLoading === 'serviceStart'} disabled={daemonHealth.status === 'running'} > Start daemonHealth.stopDaemon()} loading={operationLoading === 'serviceStop'} disabled={daemonHealth.status === 'disconnected'} variant="outline" > Stop run(alphahumanServiceStatus, 'serviceStatus')} loading={operationLoading === 'serviceStatus'} variant="outline" > Status run(alphahumanServiceInstall, 'serviceInstall')} loading={operationLoading === 'serviceInstall'} variant="outline" > Install run(alphahumanServiceUninstall, 'serviceUninstall')} loading={operationLoading === 'serviceUninstall'} variant="outline" > Uninstall {/* Auto-start Toggle */}
Auto-start Daemon
Automatically start daemon on app launch
{/* Connection Info */} {daemonHealth.connectionAttempts > 0 && (
Connection attempts: {daemonHealth.connectionAttempts}
)}
)}
{/* Category 3: Security & Data - Full width in basic mode, grid in advanced+ */} {isSectionVisible() && ( } collapsible={true} defaultExpanded={!isCollapsed('security-data')} hasChanges={false} loading={ operationLoading?.includes('Secret') || operationLoading?.includes('Models') || operationLoading?.includes('Integration') }>