// ignore_for_file: unused_import, unnecessary_import import 'dart:async'; import 'dart:convert'; import 'dart:io'; import 'package:flutter/material.dart'; import 'app_metadata.dart'; import 'app_capabilities.dart'; import 'app_store_policy.dart'; import 'ui_feature_manifest.dart'; import '../i18n/app_language.dart'; import '../models/app_models.dart'; import '../runtime/device_identity_store.dart'; import '../runtime/go_core.dart'; import '../runtime/runtime_bootstrap.dart'; import '../runtime/desktop_platform_service.dart'; import '../runtime/gateway_runtime.dart'; import '../runtime/runtime_controllers.dart'; import '../runtime/runtime_models.dart'; import '../runtime/secure_config_store.dart'; import '../runtime/embedded_agent_launch_policy.dart'; import '../runtime/runtime_coordinator.dart'; import '../runtime/gateway_acp_client.dart'; import '../runtime/codex_runtime.dart'; import '../runtime/codex_config_bridge.dart'; import '../runtime/code_agent_node_orchestrator.dart'; import '../runtime/assistant_artifacts.dart'; import '../runtime/desktop_thread_artifact_service.dart'; import '../runtime/mode_switcher.dart'; import '../runtime/agent_registry.dart'; import '../runtime/multi_agent_orchestrator.dart'; import '../runtime/platform_environment.dart'; import '../runtime/account_runtime_client.dart'; import 'app_controller_desktop_core.dart'; import 'app_controller_desktop_navigation.dart'; import 'app_controller_desktop_settings.dart'; import 'app_controller_desktop_thread_binding.dart'; import 'app_controller_desktop_thread_sessions.dart'; import 'app_controller_desktop_thread_actions.dart'; import 'app_controller_desktop_workspace_execution.dart'; import 'app_controller_desktop_settings_runtime.dart'; import 'app_controller_desktop_thread_storage.dart'; import 'app_controller_desktop_skill_permissions.dart'; import 'app_controller_desktop_runtime_helpers.dart'; extension AppControllerDesktopGateway on AppController { Future resolveConnectSetupCode(String rawInput) async { final trimmed = rawInput.trim(); return trimmed; } Future connectWithSetupCode({ required String setupCode, String token = '', String password = '', }) async { final resolvedSetupCode = await resolveConnectSetupCode(setupCode); final decoded = decodeGatewaySetupCode(resolvedSetupCode); final resolvedToken = token.trim().isNotEmpty ? token.trim() : (decoded?.token.trim() ?? ''); final resolvedPassword = password.trim().isNotEmpty ? password.trim() : (decoded?.password.trim() ?? ''); final resolvedProfileIndex = gatewayProfileIndexForExecutionTargetInternal( assistantExecutionTargetForModeInternal( modeFromHostInternal( decoded?.host ?? settings.primaryGatewayProfile.host, ), ), ); await settingsControllerInternal.saveGatewaySecrets( profileIndex: resolvedProfileIndex, token: resolvedToken, password: resolvedPassword, ); final resolvedTarget = assistantExecutionTargetForModeInternal( modeFromHostInternal( decoded?.host ?? settings.primaryGatewayProfile.host, ), ); final currentProfile = gatewayProfileForAssistantExecutionTargetInternal( resolvedTarget, ); final nextProfile = currentProfile.copyWith( useSetupCode: true, setupCode: resolvedSetupCode.trim(), host: decoded?.host ?? currentProfile.host, port: decoded?.port ?? currentProfile.port, tls: decoded?.tls ?? currentProfile.tls, mode: RuntimeConnectionMode.remote, ); await AppControllerDesktopSettings(this).saveSettings( settings .copyWithGatewayProfileAt( gatewayProfileIndexForExecutionTargetInternal(resolvedTarget), nextProfile, ) .copyWith(assistantExecutionTarget: resolvedTarget), refreshAfterSave: false, ); final sessionKey = sessionsControllerInternal.currentSessionKey; final ownerScope = await ensureDesktopThreadOwnerScopeInternal(sessionKey); final workspaceBinding = buildDesktopWorkspaceBindingInternal( sessionKey, executionTarget: resolvedTarget, ownerScope: ownerScope, ); upsertTaskThreadInternal( sessionKey, executionTarget: resolvedTarget, ownerScope: ownerScope, workspaceBinding: workspaceBinding, updatedAtMs: DateTime.now().millisecondsSinceEpoch.toDouble(), ); await AppControllerDesktopGateway(this).connectProfileInternal( nextProfile, profileIndex: resolvedProfileIndex, authTokenOverride: resolvedToken, authPasswordOverride: resolvedPassword, ); await chatControllerInternal.loadSession( sessionsControllerInternal.currentSessionKey, ); } Future connectManual({ required String host, required int port, required bool tls, required RuntimeConnectionMode mode, String token = '', String password = '', }) async { final normalizedMode = RuntimeConnectionMode.remote; final nextTarget = assistantExecutionTargetForModeInternal(normalizedMode); final nextProfileIndex = gatewayProfileIndexForExecutionTargetInternal( nextTarget, ); await settingsControllerInternal.saveGatewaySecrets( profileIndex: nextProfileIndex, token: token.trim(), password: password.trim(), ); final resolvedHost = host.trim(); final resolvedPort = port; final nextProfile = gatewayProfileForAssistantExecutionTargetInternal(nextTarget).copyWith( mode: normalizedMode, useSetupCode: false, setupCode: '', host: resolvedHost, port: resolvedPort <= 0 ? 443 : resolvedPort, tls: tls, ); await AppControllerDesktopSettings(this).saveSettings( settings .copyWithGatewayProfileAt( gatewayProfileIndexForExecutionTargetInternal(nextTarget), nextProfile, ) .copyWith(assistantExecutionTarget: nextTarget), refreshAfterSave: false, ); final sessionKey = sessionsControllerInternal.currentSessionKey; final ownerScope = await ensureDesktopThreadOwnerScopeInternal(sessionKey); final workspaceBinding = buildDesktopWorkspaceBindingInternal( sessionKey, executionTarget: nextTarget, ownerScope: ownerScope, ); upsertTaskThreadInternal( sessionKey, executionTarget: nextTarget, ownerScope: ownerScope, workspaceBinding: workspaceBinding, updatedAtMs: DateTime.now().millisecondsSinceEpoch.toDouble(), ); await AppControllerDesktopGateway(this).connectProfileInternal( nextProfile, profileIndex: nextProfileIndex, authTokenOverride: token.trim(), authPasswordOverride: password.trim(), ); await chatControllerInternal.loadSession( sessionsControllerInternal.currentSessionKey, ); } Future disconnectGateway() async { clearCodexGatewayRegistrationInternal(); await runtimeInternal.disconnect(clearDesiredProfile: false); await settingsControllerInternal.refreshDerivedState(); await agentsControllerInternal.refresh(); await sessionsControllerInternal.refresh(); chatControllerInternal.clear(); await skillsControllerInternal.refresh(); await modelsControllerInternal.refresh(); await cronJobsControllerInternal.refresh(); devicesControllerInternal.clear(); recomputeTasksInternal(); } Future connectProfileInternal( GatewayConnectionProfile profile, { int? profileIndex, String authTokenOverride = '', String authPasswordOverride = '', }) async { final resolvedProfileIndex = profileIndex ?? gatewayProfileIndexForExecutionTargetInternal( assistantExecutionTargetForModeInternal(profile.mode), ); final effectiveAuthTokenOverride = authTokenOverride.trim().isNotEmpty ? authTokenOverride.trim() : await settingsControllerInternal.loadEffectiveGatewayToken( profileIndex: resolvedProfileIndex, ); final effectiveAuthPasswordOverride = authPasswordOverride.trim().isNotEmpty ? authPasswordOverride.trim() : await settingsControllerInternal.loadEffectiveGatewayPassword( profileIndex: resolvedProfileIndex, ); await runtimeInternal.connectProfile( profile, profileIndex: resolvedProfileIndex, authTokenOverride: effectiveAuthTokenOverride, authPasswordOverride: effectiveAuthPasswordOverride, ); await refreshGatewayHealth(); await refreshAgents(); await refreshSessions(); await skillsControllerInternal.refresh( agentId: agentsControllerInternal.selectedAgentId.isEmpty ? null : agentsControllerInternal.selectedAgentId, ); await modelsControllerInternal.refresh(); await cronJobsControllerInternal.refresh(); await devicesControllerInternal.refresh(quiet: true); await settingsControllerInternal.refreshDerivedState(); try { await refreshAcpCapabilitiesInternal( forceRefresh: true, persistMountTargets: true, ); } catch (_) { // Keep the Gateway connect flow usable even if ACP capability refresh // trails the runtime handshake. } await ensureCodexGatewayRegistrationInternal(); recomputeTasksInternal(); } }