diff --git a/ios/Podfile.lock b/ios/Podfile.lock index c776d18d..573f7a01 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -1,4 +1,5 @@ PODS: + - CocoaAsyncSocket (7.6.5) - device_info_plus (0.0.1): - Flutter - file_selector_ios (0.0.1): @@ -58,6 +59,10 @@ PODS: - nanopb/encode (3.30910.0) - package_info_plus (0.4.5): - Flutter + - patrol (0.0.1): + - CocoaAsyncSocket (~> 7.6) + - Flutter + - FlutterMacOS - PromisesObjC (2.4.0) - shared_preferences_foundation (0.0.1): - Flutter @@ -73,11 +78,13 @@ DEPENDENCIES: - irondash_engine_context (from `.symlinks/plugins/irondash_engine_context/ios`) - mobile_scanner (from `.symlinks/plugins/mobile_scanner/ios`) - package_info_plus (from `.symlinks/plugins/package_info_plus/ios`) + - patrol (from `.symlinks/plugins/patrol/darwin`) - shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`) - super_native_extensions (from `.symlinks/plugins/super_native_extensions/ios`) SPEC REPOS: trunk: + - CocoaAsyncSocket - GoogleDataTransport - GoogleMLKit - GoogleToolboxForMac @@ -105,12 +112,15 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/mobile_scanner/ios" package_info_plus: :path: ".symlinks/plugins/package_info_plus/ios" + patrol: + :path: ".symlinks/plugins/patrol/darwin" shared_preferences_foundation: :path: ".symlinks/plugins/shared_preferences_foundation/darwin" super_native_extensions: :path: ".symlinks/plugins/super_native_extensions/ios" SPEC CHECKSUMS: + CocoaAsyncSocket: 065fd1e645c7abab64f7a6a2007a48038fdc6a99 device_info_plus: 21fcca2080fbcd348be798aa36c3e5ed849eefbe file_selector_ios: ec57ec07954363dd730b642e765e58f199bb621a Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467 @@ -128,6 +138,7 @@ SPEC CHECKSUMS: mobile_scanner: af8f71879eaba2bbcb4d86c6a462c3c0e7f23036 nanopb: fad817b59e0457d11a5dfbde799381cd727c1275 package_info_plus: af8e2ca6888548050f16fa2f1938db7b5a5df499 + patrol: cea8074f183a2a4232d0ebd10569ae05149ada42 PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47 shared_preferences_foundation: 7036424c3d8ec98dfe75ff1667cb0cd531ec82bb super_native_extensions: b763c02dc3a8fd078389f410bf15149179020cb4 diff --git a/lib/features/settings/settings_page_gateway.dart b/lib/features/settings/settings_page_gateway.dart index 7dadd61c..c45b02ef 100644 --- a/lib/features/settings/settings_page_gateway.dart +++ b/lib/features/settings/settings_page_gateway.dart @@ -37,13 +37,6 @@ extension SettingsPageGatewayMixinInternal on SettingsPageStateInternal { ) { if (!widget.showSectionTabs) { return [ - buildGatewayOverviewCardInternal( - context, - controller, - settings, - uiFeatures, - ), - const SizedBox(height: 16), buildOnlineAccountCardInternal(context, controller, settings), const SizedBox(height: 16), buildAcpBridgeServerModeCardInternal( @@ -95,13 +88,6 @@ extension SettingsPageGatewayMixinInternal on SettingsPageStateInternal { ), }; return [ - buildGatewayOverviewCardInternal( - context, - controller, - settings, - uiFeatures, - ), - const SizedBox(height: 16), SectionTabs( items: [ appText('用户登录状态', 'User Login State'), @@ -145,10 +131,10 @@ extension SettingsPageGatewayMixinInternal on SettingsPageStateInternal { GatewayIntegrationSubTabInternal.llm => const [], GatewayIntegrationSubTabInternal.acp => const [], GatewayIntegrationSubTabInternal.skills => const [], - GatewayIntegrationSubTabInternal.advancedConfig => - uiFeatures.supportsGatewayAdvancedCustomMode - ? [ - ...buildGatewayAdvancedSectionsInternal( + GatewayIntegrationSubTabInternal.advancedConfig => + uiFeatures.supportsGatewayAdvancedCustomMode + ? [ + ...buildGatewayAdvancedSectionsInternal( context, controller, settings, @@ -160,85 +146,6 @@ extension SettingsPageGatewayMixinInternal on SettingsPageStateInternal { ]; } - Widget buildGatewayOverviewCardInternal( - BuildContext context, - AppController controller, - SettingsSnapshot settings, - UiFeatureAccess uiFeatures, - ) { - final accountController = controller.settingsController; - final modeConfig = settings.acpBridgeServerModeConfig; - final supportsSelfHosted = uiFeatures.supportsGatewaySelfHostedBase; - final supportsAdvancedOverrides = - uiFeatures.supportsGatewayAdvancedCustomMode; - final loginStatus = accountController.accountMfaRequired - ? appText('MFA 待验证', 'MFA pending') - : accountController.accountBusy - ? appText('登录中', 'Signing in') - : accountController.accountSignedIn - ? appText('已登录', 'Signed in') - : appText('未登录', 'Signed out'); - final defaultSource = supportsSelfHosted && modeConfig.usesSelfHostedBase - ? appText('自建服务', 'Self-hosted') - : appText('svc.plus 提供', 'svc.plus provided'); - final hasAdvancedOverrides = - supportsAdvancedOverrides && - modeConfig.mode == AcpBridgeServerMode.advancedCustom; - return SurfaceCard( - key: const ValueKey('gateway-configuration-overview-card'), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - appText('最终生效配置概览', 'Effective Configuration Overview'), - style: Theme.of(context).textTheme.titleLarge, - ), - const SizedBox(height: 8), - Text( - supportsAdvancedOverrides - ? appText( - '先确认登录状态,再确定默认连接来源,最后按需用高级自定义模式覆盖默认配置。', - 'Confirm login state first, choose the default connection source second, then apply advanced custom overrides only where needed.', - ) - : appText( - '先确认登录状态,再查看当前默认连接来源。', - 'Confirm login state first, then review the current default connection source.', - ), - ), - const SizedBox(height: 16), - Wrap( - spacing: 12, - runSpacing: 12, - children: [ - StatusChipInternal( - key: const ValueKey('gateway-overview-login-status'), - label: '${appText('登录状态', 'Login')}: $loginStatus', - tone: accountController.accountSignedIn - ? StatusChipToneInternal.ready - : StatusChipToneInternal.idle, - ), - StatusChipInternal( - key: const ValueKey('gateway-overview-default-source'), - label: - '${appText('默认连接来源', 'Default Source')}: $defaultSource', - tone: StatusChipToneInternal.ready, - ), - if (supportsAdvancedOverrides) - StatusChipInternal( - key: const ValueKey('gateway-overview-advanced-override'), - label: - '${appText('高级覆盖', 'Advanced Override')}: ${hasAdvancedOverrides ? appText('已启用', 'Enabled') : appText('未启用', 'Disabled')}', - tone: hasAdvancedOverrides - ? StatusChipToneInternal.ready - : StatusChipToneInternal.idle, - ), - ], - ), - ], - ), - ); - } - List buildGatewayAdvancedSectionsInternal( BuildContext context, AppController controller, diff --git a/scripts/package-flutter-mac-app.sh b/scripts/package-flutter-mac-app.sh index 65d47773..855aa4cf 100755 --- a/scripts/package-flutter-mac-app.sh +++ b/scripts/package-flutter-mac-app.sh @@ -14,6 +14,32 @@ FLUTTER_BUILD_STATE_DIR="${ROOT_DIR}/.dart_tool/flutter_build" MACOS_BUILD_DIR="${ROOT_DIR}/build/macos" NATIVE_ASSETS_DIR="${ROOT_DIR}/build/native_assets" +remove_tree_with_retries() { + local path="$1" + local attempts="${2:-5}" + local delay_seconds="${3:-1}" + local try=1 + + [[ -e "$path" ]] || return 0 + + while (( try <= attempts )); do + chmod -R u+w "$path" 2>/dev/null || true + rm -rf "$path" 2>/dev/null || true + + if [[ ! -e "$path" ]]; then + return 0 + fi + + if (( try == attempts )); then + echo "Failed to remove generated path after ${attempts} attempts: $path" >&2 + return 1 + fi + + sleep "$delay_seconds" + ((try++)) + done +} + if [[ ! -f "$PUBSPEC_PATH" ]]; then echo "Missing pubspec: $PUBSPEC_PATH" >&2 exit 1 @@ -40,7 +66,9 @@ echo "Building $APP_NAME $APP_VERSION ($APP_BUILD) for macOS..." # Flutter caches native-asset installation state under .dart_tool/flutter_build, # but Xcode consumes the copied frameworks from build/native_assets/macos. # Reset both locations so packaging cannot reuse a stale stamp or stale layout. -rm -rf "$FLUTTER_BUILD_STATE_DIR" "$MACOS_BUILD_DIR" "$NATIVE_ASSETS_DIR" +remove_tree_with_retries "$FLUTTER_BUILD_STATE_DIR" +remove_tree_with_retries "$MACOS_BUILD_DIR" +remove_tree_with_retries "$NATIVE_ASSETS_DIR" BUILD_ARGS=( flutter build macos diff --git a/test/features/settings_page_suite.dart b/test/features/settings_page_suite.dart index 42f515af..6f5ae811 100644 --- a/test/features/settings_page_suite.dart +++ b/test/features/settings_page_suite.dart @@ -234,15 +234,15 @@ void main() { expect( find.byKey(const ValueKey('gateway-configuration-overview-card')), - findsOneWidget, + findsNothing, ); expect( find.byKey(const ValueKey('gateway-overview-login-status')), - findsOneWidget, + findsNothing, ); expect( find.byKey(const ValueKey('gateway-overview-default-source')), - findsOneWidget, + findsNothing, ); expect( find.byKey(const ValueKey('gateway-overview-advanced-override')), @@ -415,7 +415,7 @@ void main() { expect(find.byKey(const ValueKey('account-base-url-field')), findsOneWidget); expect(find.byKey(const ValueKey('account-username-field')), findsOneWidget); expect(find.byKey(const ValueKey('account-password-field')), findsOneWidget); - expect(find.byKey(const ValueKey('acp-bridge-mode-cloud')), findsNothing); + expect(find.byKey(const ValueKey('acp-bridge-mode-cloud')), findsOneWidget); expect( find.byKey(const ValueKey('acp-bridge-mode-self-hosted')), findsNothing, @@ -437,9 +437,6 @@ void main() { await _pumpSettingsPage(tester, controller, tab: SettingsTab.gateway); - await tester.tap(find.byKey(const ValueKey('section-tab-高级自定义模式'))); - await tester.pumpAndSettle(); - expect(find.text('Vault Server'), findsAtLeastNWidgets(1)); expect(find.text('VAULT_SERVER_URL'), findsOneWidget); expect( @@ -460,9 +457,6 @@ void main() { await _pumpSettingsPage(tester, controller, tab: SettingsTab.gateway); - await tester.tap(find.byKey(const ValueKey('section-tab-高级自定义模式'))); - await tester.pumpAndSettle(); - expect(find.text('外部 ACP Server Endpoint'), findsOneWidget); expect(find.textContaining('Codex'), findsWidgets); expect(find.textContaining('OpenCode'), findsWidgets); @@ -496,9 +490,6 @@ void main() { await _pumpSettingsPage(tester, controller, tab: SettingsTab.gateway); - await tester.tap(find.byKey(const ValueKey('section-tab-高级自定义模式'))); - await tester.pumpAndSettle(); - await _ensureVisible( tester, find.byKey(const ValueKey('external-acp-provider-add-button')), @@ -536,9 +527,6 @@ void main() { await _pumpSettingsPage(tester, controller, tab: SettingsTab.gateway); - await tester.tap(find.byKey(const ValueKey('section-tab-高级自定义模式'))); - await tester.pumpAndSettle(); - expect(find.text('~/.agents/skills'), findsOneWidget); expect(find.text('/Users/tester/.agents/skills'), findsOneWidget); expect(find.text('~/.codex/skills'), findsOneWidget); @@ -569,8 +557,6 @@ void main() { ); await _pumpSettingsPage(tester, controller, tab: SettingsTab.gateway); - await tester.tap(find.byKey(const ValueKey('section-tab-高级自定义模式'))); - await tester.pumpAndSettle(); await _ensureVisible( tester, find.byKey(const ValueKey('skill-directory-batch-add-button')), @@ -629,8 +615,6 @@ paths: ); await _pumpSettingsPage(tester, controller, tab: SettingsTab.gateway); - await tester.tap(find.byKey(const ValueKey('section-tab-高级自定义模式'))); - await tester.pumpAndSettle(); await _ensureVisible( tester, find.byKey(const ValueKey('skill-directory-batch-add-button')), @@ -672,8 +656,6 @@ paths: ); await _pumpSettingsPage(tester, controller, tab: SettingsTab.gateway); - await tester.tap(find.byKey(const ValueKey('section-tab-高级自定义模式'))); - await tester.pumpAndSettle(); await _ensureVisible( tester, find.byKey(const ValueKey('skill-directory-batch-add-button')), @@ -712,11 +694,14 @@ paths: testWidgets('SettingsPage gateway sections can collapse individually', ( WidgetTester tester, ) async { - final controller = await createTestController(tester); + final controller = await createTestController( + tester, + uiFeatureManifest: _gatewayAdvancedManifestInternal(), + ); await _pumpSettingsPage(tester, controller, tab: SettingsTab.gateway); - await tester.tap(find.byTooltip('折叠').first); + await tester.tap(find.text('OpenClaw Gateway').first); await tester.pumpAndSettle(); expect(find.byKey(const ValueKey('gateway-host-field')), findsNothing); @@ -726,7 +711,7 @@ paths: findsNothing, ); - await tester.tap(find.byTooltip('展开').first); + await tester.tap(find.text('OpenClaw Gateway').first); await tester.pumpAndSettle(); expect(find.byKey(const ValueKey('gateway-host-field')), findsOneWidget); @@ -746,8 +731,6 @@ paths: ); await _pumpSettingsPage(tester, controller, tab: SettingsTab.gateway); - await tester.tap(find.byKey(const ValueKey('section-tab-高级自定义模式'))); - await tester.pumpAndSettle(); await _ensureVisible(tester, find.text('外部 ACP Server Endpoint')); await tester.tap(find.text('外部 ACP Server Endpoint').first); @@ -791,8 +774,6 @@ paths: ); await _pumpSettingsPage(tester, controller, tab: SettingsTab.gateway); - await tester.tap(find.byKey(const ValueKey('section-tab-高级自定义模式'))); - await tester.pumpAndSettle(); final labelField = find.byKey( ValueKey('external-acp-label-${customProfile.providerKey}'),