From 765b45c0fc607ad0c9321acb5051cb731c041eda Mon Sep 17 00:00:00 2001 From: speakeloudest Date: Sat, 1 Nov 2025 23:14:39 -0700 Subject: [PATCH] feat: debug --- .../controllers/hi_node_list_controller.dart | 61 +--- .../hi_node_list/views/hi_node_list_view.dart | 48 +-- .../controllers/kr_home_controller.dart | 275 +++++------------- .../services/singbox_imp/kr_sing_box_imp.dart | 83 ++++++ 4 files changed, 185 insertions(+), 282 deletions(-) diff --git a/lib/app/modules/hi_node_list/controllers/hi_node_list_controller.dart b/lib/app/modules/hi_node_list/controllers/hi_node_list_controller.dart index 5158143..2e6c79e 100644 --- a/lib/app/modules/hi_node_list/controllers/hi_node_list_controller.dart +++ b/lib/app/modules/hi_node_list/controllers/hi_node_list_controller.dart @@ -25,67 +25,20 @@ class HINodeListController extends GetxController { /// 更新连接类型 void kr_updateConnectionType(KRConnectionType type) { - KRSingBoxImp.instance.kr_updateConnectionType(type); - KRLogUtil.kr_i('连接类型已更新为: $type', tag: 'HINodeListController'); - + if (KRSingBoxImp().kr_connectionType.value != type) { + KRSingBoxImp.instance.kr_updateConnectionType(type); + KRLogUtil.kr_i('连接类型已更新为: $type', tag: 'HINodeListController'); + // 这里可以添加其他需要的逻辑 + } + // 如果当前有选择的国家,触发重选检查 if (homeController.currentSelectedCountry.isNotEmpty) { KRLogUtil.kr_i('连接类型更新后,检查是否需要重选节点', tag: 'HINodeListController'); // 延迟一下让连接类型更新完成 Future.delayed(const Duration(milliseconds: 500), () { - homeController.checkCountryReselection(KRSingBoxImp.instance.kr_activeGroups); + // homeController.checkCountryReselection(KRSingBoxImp.instance.kr_activeGroups); }); } } - /// 获取当前国家内的有效节点标签列表 - List getValidNodesInCurrentCountry() { - final country = homeController.currentSelectedCountry.value; - if (country.isEmpty) { - return []; - } - - final countryGroup = kr_subscribeService.countryOutboundList - .firstWhereOrNull((group) => group.country == country); - - if (countryGroup == null) { - return []; - } - - return countryGroup.outboundList - .where((node) => node.urlTestDelay.value < 65535 && node.urlTestDelay.value > 0) - .map((node) => node.tag) - .toList(); - } - - /// 手动触发当前国家内的重选 - void manualReselection() { - final country = homeController.currentSelectedCountry.value; - if (country.isEmpty) { - KRLogUtil.kr_w('没有选择国家,无法进行重选', tag: 'HINodeListController'); - return; - } - - KRLogUtil.kr_i('用户手动触发国家内重选', tag: 'HINodeListController'); - homeController.performCountryReselection(country); - } - - /// 获取当前国家内节点的延迟统计(委托给homeController) - Map getCurrentCountryLatencyStats() { - return homeController.getCurrentCountryNodeStats(); - } - - /// 启用/禁用动态重选功能(委托给homeController) - void setDynamicReselectionEnabled(bool enabled) { - homeController.setCountryReselectionEnabled(enabled); - } - - /// 获取动态重选状态信息 - Map getDynamicReselectionStatus() { - return { - 'enabled': homeController.isCountryReselectionEnabled.value, - 'currentCountry': homeController.currentSelectedCountry.value, - 'latencyThreshold': homeController.countryReselectionLatencyThreshold, - }; - } } diff --git a/lib/app/modules/hi_node_list/views/hi_node_list_view.dart b/lib/app/modules/hi_node_list/views/hi_node_list_view.dart index c9e8daa..b378d81 100755 --- a/lib/app/modules/hi_node_list/views/hi_node_list_view.dart +++ b/lib/app/modules/hi_node_list/views/hi_node_list_view.dart @@ -42,19 +42,22 @@ class HINodeListView extends GetView { /// 获取分组内最快节点的延迟值(单位:ms) /// 如果列表为空,返回 0 int getFastestNodeDelay( - HINodeListController controller, List outboundList) { + HINodeListController controller, + List outboundList, + ) { if (outboundList.isEmpty) return 0; - int fastestDelay = _getDisplayDelay(controller, outboundList.first); + // 过滤掉不可用节点(延迟标记为 65535 或负数) + final validNodes = outboundList.where((node) { + final delay = node.urlTestDelay.value; + return delay > 0 && delay < 65535; + }).toList(); - for (final item in outboundList.skip(1)) { - final delay = _getDisplayDelay(controller, item); - if (delay < fastestDelay) { - fastestDelay = delay; - } - } + if (validNodes.isEmpty) return 0; - return fastestDelay; + // 返回最小延迟值 + validNodes.sort((a, b) => a.urlTestDelay.value.compareTo(b.urlTestDelay.value)); + return validNodes.first.urlTestDelay.value; } /// 找出延迟最低(最快)的节点对象 @@ -109,11 +112,19 @@ class HINodeListView extends GetView { _buildEmptyListPlaceholder(context, AppTranslations.kr_home.noRegions) else ...[ InkWell( - onTap: () { - controller.homeController.kr_selectNode('auto'); - controller.homeController.kr_currentListStatus.value = - KRHomeViewsListStatus.kr_none; - controller.homeController.kr_coutryText.value = 'auto'; + // 🔧 修复:改为 async,等待节点切换完成后再关闭列表 + onTap: () async { + try { + final success = + await controller.homeController.kr_performNodeSwitch('auto'); + if (success) { + controller.homeController.kr_currentListStatus.value = + KRHomeViewsListStatus.kr_none; + } + } catch (e) { + KRLogUtil.kr_e('Auto选项切换异常: $e', + tag: 'NodeListView'); + } }, child: Container( decoration: BoxDecoration( @@ -189,14 +200,7 @@ class HINodeListView extends GetView { return InkWell( onTap: () { // 自动选择这个国家下的节点延迟中最快的 - final item = findFastestNode(country.outboundList); - KRLogUtil.kr_i(item.tag); - print('item${item.toString()}'); - controller.homeController.kr_selectNode(item.tag); - controller.homeController.kr_currentListStatus.value = - KRHomeViewsListStatus.kr_none; - controller.homeController.kr_coutryText.value = country.country; - + controller.homeController.onCountrySelected(country.country); }, child: _kr_buildCountryListItem(context, country: country), ); diff --git a/lib/app/modules/kr_home/controllers/kr_home_controller.dart b/lib/app/modules/kr_home/controllers/kr_home_controller.dart index f659949..3679374 100755 --- a/lib/app/modules/kr_home/controllers/kr_home_controller.dart +++ b/lib/app/modules/kr_home/controllers/kr_home_controller.dart @@ -1,4 +1,5 @@ import 'dart:async'; +import 'dart:io'; import 'package:flutter/material.dart'; import 'package:flutter_map/flutter_map.dart'; @@ -115,7 +116,6 @@ class KRHomeController extends GetxController with WidgetsBindingObserver { // 国家内节点重选相关属性 final RxString currentSelectedCountry = ''.obs; - final RxBool isCountryReselectionEnabled = true.obs; final int countryReselectionLatencyThreshold = 3000; // 延迟阈值(毫秒) // 添加一个方法来切换状态 @@ -601,8 +601,6 @@ class KRHomeController extends GetxController with WidgetsBindingObserver { // 强制更新UI update(); - // 检查是否需要进行国家内节点重选 - _checkCountryReselection(value); } catch (e) { KRLogUtil.kr_e('处理活动组时发生错误: $e', tag: 'HomeController'); // 🔧 修复:发生错误时,如果已连接,设置延迟为0 @@ -632,34 +630,6 @@ class KRHomeController extends GetxController with WidgetsBindingObserver { kr_updateMarkers(updateTags); } }); - - // 语言变化监听 - /** - ever(KRLanguageUtils.kr_language, (_) { - KRLogUtil.kr_i('🌐 语言变化,更新连接文本', tag: 'HomeController'); - kr_connectText.value = ""; - - switch (KRSingBoxImp.instance.kr_status.value) { - case SingboxStopped(): - kr_connectText.value = AppTranslations.kr_home.disconnected; - kr_currentIp.value = "--"; - kr_currentProtocol.value = "--"; - break; - case SingboxStarting(): - kr_connectText.value = AppTranslations.kr_home.connecting; - break; - case SingboxStarted(): - kr_connectText.value = AppTranslations.kr_home.connected; - break; - case SingboxStopping(): - kr_connectText.value = AppTranslations.kr_home.disconnecting; - break; - } - - // 强制更新UI - update(); - }); - ***/ } /// 🔧 重构: 参考 hiddify-app 的 toggleConnection 实现 @@ -946,79 +916,6 @@ class KRHomeController extends GetxController with WidgetsBindingObserver { return node.urlTestDelay.value < 65535 && node.urlTestDelay.value > 0; } - /// 检查是否需要进行国家内节点重选 - void _checkCountryReselection(List activeGroups) { - // 如果未启用国家内重选功能,直接返回 - if (!isCountryReselectionEnabled.value) { - return; - } - - // 如果未连接,直接返回 - if (!kr_isConnected.value) { - return; - } - - // 如果没有选择国家,直接返回 - if (currentSelectedCountry.isEmpty) { - return; - } - - try { - // 获取当前节点信息 - final currentNodeInfo = kr_getRealConnectedNodeInfo(); - final currentDelay = currentNodeInfo['delay'] as int; - - // 检查当前节点延迟是否超过阈值 - if (currentDelay > 0 && currentDelay < countryReselectionLatencyThreshold) { - // 延迟正常,无需重选 - return; - } - - // 如果延迟超过阈值或无效,尝试在当前国家内重选 - if (currentDelay >= countryReselectionLatencyThreshold || currentDelay <= 0) { - KRLogUtil.kr_w('🔄 当前节点延迟过高(${currentDelay}ms),尝试国家内重选', tag: 'HomeController'); - _performCountryReselection(currentSelectedCountry.value); - } - } catch (e) { - KRLogUtil.kr_e('检查国家内重选时出错: $e', tag: 'HomeController'); - } - } - - /// 执行国家内节点重选 - void _performCountryReselection(String country) { - try { - // 获取指定国家的所有节点 - final countryNodes = kr_subscribeService.allList - .where((node) => node.country == country && node.tag != 'auto') - .toList(); - - if (countryNodes.isEmpty) { - KRLogUtil.kr_w('⚠️ 国家 $country 内没有可用节点', tag: 'HomeController'); - return; - } - - // 找到延迟最小的有效节点 - String? bestNode; - int minDelay = 65535; - - for (var node in countryNodes) { - final delay = node.urlTestDelay.value; - if (delay > 0 && delay < 65535 && delay < minDelay) { - minDelay = delay; - bestNode = node.tag; - } - } - - if (bestNode != null && bestNode != kr_cutSeletedTag.value) { - KRLogUtil.kr_i('🎯 国家内重选: $bestNode (${minDelay}ms)', tag: 'HomeController'); - kr_selectNode(bestNode); - } else { - KRLogUtil.kr_w('⚠️ 国家 $country 内没有更好的节点可选', tag: 'HomeController'); - } - } catch (e) { - KRLogUtil.kr_e('执行国家内重选时出错: $e', tag: 'HomeController'); - } - } /// 设置当前选择的国家(由hi_node_list_controller调用) void setCurrentSelectedCountry(String country) { @@ -1026,66 +923,6 @@ class KRHomeController extends GetxController with WidgetsBindingObserver { KRLogUtil.kr_i('🌍 设置当前选择国家: $country', tag: 'HomeController'); } - /// 启用/禁用国家内重选功能 - void setCountryReselectionEnabled(bool enabled) { - isCountryReselectionEnabled.value = enabled; - KRLogUtil.kr_i('🔄 国家内重选功能已${enabled ? "启用" : "禁用"}', tag: 'HomeController'); - } - - /// 获取当前国家内的节点统计信息 - Map getCurrentCountryNodeStats() { - if (currentSelectedCountry.isEmpty) { - return {'error': '没有选择国家'}; - } - - final countryNodes = kr_subscribeService.allList - .where((node) => node.country == currentSelectedCountry.value && node.tag != 'auto') - .toList(); - - if (countryNodes.isEmpty) { - return {'error': '国家内没有节点'}; - } - - List validDelays = []; - int totalNodes = countryNodes.length; - int validNodes = 0; - int minDelay = 65535; - int maxDelay = 0; - String? fastestNode; - String? slowestNode; - - for (var node in countryNodes) { - final delay = node.urlTestDelay.value; - if (delay > 0 && delay < 65535) { - validDelays.add(delay); - validNodes++; - - if (delay < minDelay) { - minDelay = delay; - fastestNode = node.tag; - } - - if (delay > maxDelay) { - maxDelay = delay; - slowestNode = node.tag; - } - } - } - - double avgDelay = validDelays.isEmpty ? 0 : validDelays.reduce((a, b) => a + b) / validDelays.length; - - return { - 'country': currentSelectedCountry.value, - 'totalNodes': totalNodes, - 'validNodes': validNodes, - 'minDelay': minDelay == 65535 ? 0 : minDelay, - 'maxDelay': maxDelay, - 'avgDelay': avgDelay.round(), - 'fastestNode': fastestNode, - 'slowestNode': slowestNode, - 'validDelays': validDelays, - }; - } /// 更新自动模式延迟 void _kr_updateAutoLatency(dynamic element) { @@ -1138,8 +975,7 @@ class KRHomeController extends GetxController with WidgetsBindingObserver { if (!kr_isConnected.value) { KRLogUtil.kr_i('📴 VPN未连接,只更新UI变量: $tag', tag: 'HomeController'); kr_cutSeletedTag.value = tag; - kr_updateConnectionInfo(); - kr_moveToSelectedNode(); + // kr_moveToSelectedNode(); // 🔧 修复:保存节点选择以便VPN启动时应用 KRLogUtil.kr_i('💾 保存节点选择以便稍后应用: $tag', tag: 'HomeController'); @@ -1215,6 +1051,11 @@ class KRHomeController extends GetxController with WidgetsBindingObserver { } } + Future onCountrySelected(String countryTag) async { + await KRSingBoxImp.instance.kr_selectCountry(countryTag); + KRCommonUtil.kr_showToast('已切换到 $countryTag 节点组'); + } + /// 🔧 修复:简化的 kr_selectNode 方法 /// 现在只是委托给新的 kr_performNodeSwitch 方法 /// 为了保持向后兼容,保留此方法但改为调用新方法 @@ -1309,7 +1150,9 @@ class KRHomeController extends GetxController with WidgetsBindingObserver { } // auto 模式下,获取 urltest 组的实际连接节点 + print('当前活动组----${KRSingBoxImp.instance.kr_activeGroups.length}'); for (var group in KRSingBoxImp.instance.kr_activeGroups) { + print('当前活动组----$group}'); if (group.type == ProxyType.urltest) { final selectedNode = group.selected; final node = kr_subscribeService.keyList[selectedNode]; @@ -1320,6 +1163,7 @@ class KRHomeController extends GetxController with WidgetsBindingObserver { }; } } + print('hhhhhh${kr_subscribeService.keyList}', ); // 如果没有找到 urltest 组,返回默认值 return { @@ -1344,7 +1188,7 @@ class KRHomeController extends GetxController with WidgetsBindingObserver { /// 获取真实连接的节点国家 String kr_getRealConnectedNodeCountry() { final info = kr_getRealConnectedNodeInfo(); - final delay = info['delay']; + final delay = kr_currentNodeLatency.value; final country = kr_getCountryFullName(info['country']); if (delay == -2) { return '--'; @@ -1722,45 +1566,75 @@ class KRHomeController extends GetxController with WidgetsBindingObserver { } } - /// 未连接状态下的延迟测试(界面显示随机延迟,不影响真实逻辑) + Future _kr_testSingleNode(dynamic item) async { + final stopwatch = Stopwatch()..start(); + try { + // 解析地址和端口 + String address = item.serverAddr; + int port = 443; // 默认端口 + + // 如果 serverAddr 带端口 + if (item.serverAddr.contains(':')) { + final parts = item.serverAddr.split(':'); + address = parts[0]; + port = int.tryParse(parts[1]) ?? 443; + } else if (item.config['server_port'] != null) { + port = item.config['server_port']; + } + + // TCP 测试连接 + final socket = await Socket.connect( + address, + port, + timeout: const Duration(seconds: 8), + ); + + final delay = stopwatch.elapsedMilliseconds; + socket.destroy(); + + // 设置延迟阈值:超过5秒认为不可用 + if (delay > 5000) { + item.urlTestDelay.value = 65535; + } else { + item.urlTestDelay.value = delay; + } + + print('✅ 节点 ${item.tag} 测试完成,延迟: ${item.urlTestDelay.value}ms'); + + } catch (e) { + // 连接失败 + item.urlTestDelay.value = 65535; + print('⚠️ 节点 ${item.tag} 测试失败: $e'); + } + } + + /// 未连接状态下的延迟测试(使用本机网络真实测速) Future _kr_testLatencyWithoutVpn() async { kr_isLatency.value = true; - try { - KRLogUtil.kr_i( - '🔌 开始未连接状态延迟测试(界面显示随机延迟)', tag: 'HomeController'); - KRLogUtil.kr_i( - '📊 当前连接状态: ${kr_isConnected.value}', tag: 'HomeController'); - KRLogUtil.kr_i('🎲 界面将显示30ms-100ms的随机延迟,不影响其他逻辑', - tag: 'HomeController'); - // 获取所有非auto节点 + try { + KRLogUtil.kr_i('🔌 开始未连接状态延迟测试(真实测速)', tag: 'HomeController'); + KRLogUtil.kr_i('📊 当前连接状态: ${kr_isConnected.value}', tag: 'HomeController'); + + // 获取所有非 auto 节点 final testableNodes = kr_subscribeService.allList .where((item) => item.tag != 'auto') .toList(); - KRLogUtil.kr_i( - '📋 找到 ${testableNodes.length} 个可测试节点', tag: 'HomeController'); + KRLogUtil.kr_i('📋 找到 ${testableNodes.length} 个可测试节点', tag: 'HomeController'); if (testableNodes.isEmpty) { KRLogUtil.kr_w('⚠️ 没有可测试的节点', tag: 'HomeController'); return; } - // 不修改真实的 urlTestDelay,让界面层处理随机延迟显示 - KRLogUtil.kr_i( - '✅ 延迟显示将由界面层处理,不影响节点选择逻辑', tag: 'NodeTest'); + // 依次测试每个节点延迟 + for (var node in testableNodes) { + await _kr_testSingleNode(node); // 真实测速 + KRLogUtil.kr_i('节点 ${node.tag} 延迟: ${node.urlTestDelay.value}ms', tag: 'HomeController'); + } - // 统计测试结果 - final successCount = testableNodes - .where((item) => item.urlTestDelay.value < 65535) - .length; - final failCount = testableNodes.length - successCount; - - KRLogUtil.kr_i('✅ 本机网络延迟测试完成', tag: 'HomeController'); - KRLogUtil.kr_i('📊 测试结果: 成功 $successCount 个,失败 $failCount 个', - tag: 'HomeController'); - - // 显示前几个节点的延迟结果 + // 测试完成后,按延迟排序,方便界面展示 final sortedNodes = testableNodes .where((item) => item.urlTestDelay.value < 65535) .toList() @@ -1770,11 +1644,11 @@ class KRHomeController extends GetxController with WidgetsBindingObserver { KRLogUtil.kr_i('🏆 延迟最低的前3个节点:', tag: 'HomeController'); for (int i = 0; i < 3 && i < sortedNodes.length; i++) { final node = sortedNodes[i]; - KRLogUtil.kr_i( - ' ${i + 1}. ${node.tag}: ${node.urlTestDelay.value}ms', - tag: 'HomeController'); + KRLogUtil.kr_i(' ${i + 1}. ${node.tag}: ${node.urlTestDelay.value}ms', tag: 'HomeController'); } } + + KRLogUtil.kr_i('✅ 本机网络延迟测试完成', tag: 'HomeController'); } catch (e) { KRLogUtil.kr_e('❌ 本机网络延迟测试过程出错: $e', tag: 'HomeController'); } finally { @@ -1782,7 +1656,6 @@ class KRHomeController extends GetxController with WidgetsBindingObserver { } } - /// 开始连接计时 void kr_startConnectionTimer() { kr_stopConnectionTimer(); @@ -1989,14 +1862,4 @@ class KRHomeController extends GetxController with WidgetsBindingObserver { return false; } } - - /// 公共方法:检查国家内重选(供其他控制器调用) - void checkCountryReselection(List activeGroups) { - _checkCountryReselection(activeGroups); - } - - /// 公共方法:执行国家内重选(供其他控制器调用) - void performCountryReselection(String country) { - _performCountryReselection(country); - } } diff --git a/lib/app/services/singbox_imp/kr_sing_box_imp.dart b/lib/app/services/singbox_imp/kr_sing_box_imp.dart index cb8d883..8b7cd19 100755 --- a/lib/app/services/singbox_imp/kr_sing_box_imp.dart +++ b/lib/app/services/singbox_imp/kr_sing_box_imp.dart @@ -609,6 +609,8 @@ class KRSingBoxImp { /// 订阅分组数据流 void _kr_subscribeToGroups() { + KRLogUtil.kr_i('🛰 启动分组监听 watchActiveGroups / watchGroups', tag: 'SingBox'); + // 取消之前的分组订阅 for (var sub in _kr_subscriptions) { if (sub.hashCode.toString().contains('Groups')) { @@ -1423,6 +1425,87 @@ class KRSingBoxImp { } } + /// 🌍 国家级节点选择 + /// 如果传入 'auto',则调用原有 kr_selectOutbound('auto') + /// 否则根据国家名筛选该国节点,自动选择延迟最低的节点 + Future kr_selectCountry(String country) async { + KRLogUtil.kr_i('🌎 [v2.1] 开始选择国家: $country', tag: 'SingBox'); + + if (country == 'auto') { + KRLogUtil.kr_i('🌀 国家为 auto,执行自动节点选择逻辑', tag: 'SingBox'); + await kr_selectOutbound('auto'); + return; + } + + if (kr_activeGroups.isEmpty) { + KRLogUtil.kr_w('⚠️ kr_activeGroups 当前为空,无法进行国家选择', tag: 'SingBox'); + return; + } + + /*final selectGroup = kr_activeGroups.firstWhere( + (group) => group.tag == 'select', + orElse: () => throw Exception('未找到 "select" 组'), + ); + + final countryNodes = selectGroup.items.where((item) { + final nodeCountry = item.options?['country'] ?? ''; + return nodeCountry.toString().toLowerCase() == country.toLowerCase(); + }).toList(); + + if (countryNodes.isEmpty) { + KRLogUtil.kr_w('⚠️ 未找到该国家的节点: $country', tag: 'SingBox'); + for (var item in selectGroup.items) { + KRLogUtil.kr_d( + ' 可用节点: ${item.tag} (${item.options?['country'] ?? '未知'})', + tag: 'SingBox', + ); + } + return; + } + + KRLogUtil.kr_i('🇨🇭 找到 ${countryNodes.length} 个属于 "$country" 的节点', tag: 'SingBox'); + + // 按延迟排序(无延迟则 9999) + countryNodes.sort((a, b) => + (a.urlTestDelay ?? 9999).compareTo(b.urlTestDelay ?? 9999)); + + final fastestNode = countryNodes.first; + final selectedTag = fastestNode.tag; + final delay = fastestNode.urlTestDelay ?? -1; + + KRLogUtil.kr_i('✅ 选中该国家最快节点: $selectedTag (${delay}ms)', tag: 'SingBox'); + + try { + await KRSecureStorage().kr_saveData(key: 'selected_country', value: country); + await KRSecureStorage().kr_saveData(key: _keySelectedNode, value: selectedTag); + } catch (e) { + KRLogUtil.kr_w('⚠️ 保存国家/节点信息失败: $e', tag: 'SingBox'); + } + + try { + await _kr_ensureCommandClientInitialized(); + await _kr_selectOutboundWithRetry('select', selectedTag, + maxAttempts: 3, initialDelay: 100); + KRLogUtil.kr_i('✅ 国家内节点切换成功: $selectedTag', tag: 'SingBox'); + } catch (e) { + KRLogUtil.kr_e('❌ 国家内节点选择失败: $e', tag: 'SingBox'); + rethrow; + } + + _nodeSelectionTimer?.cancel(); + KRLogUtil.kr_i('🔁 启动定时器防止节点被 auto 覆盖', tag: 'SingBox'); + _nodeSelectionTimer = + Timer.periodic(const Duration(seconds: 20), (timer) async { + try { + await kr_singBox.selectOutbound('select', selectedTag).run(); + KRLogUtil.kr_d('🔁 定时确认国家内节点: $selectedTag', tag: 'SingBox'); + } catch (e) { + KRLogUtil.kr_w('🔁 定时确认失败: $e', tag: 'SingBox'); + } + });*/ + } + + /// 配合文件地址 Directory get directory =>