diff --git a/client/macos/PacketTunnel/PacketTunnelProvider.swift b/client/macos/PacketTunnel/PacketTunnelProvider.swift index 09dfca9..9c2fe77 100644 --- a/client/macos/PacketTunnel/PacketTunnelProvider.swift +++ b/client/macos/PacketTunnel/PacketTunnelProvider.swift @@ -445,24 +445,37 @@ final class StatsCollector: NSObject { } private func pollClash() { - // 主源:读 /proxies 里各 URLTest 成员的 history 缓存延迟(稳定,active 探测超时也不丢)。 - // 同时对 URLTest 组主动跑一次 /group//delay 刷新 history(跳过 GLOBAL 选择器组)。 - clashGet("/proxies", query: nil) { [weak self] obj, _ in + // 先取组名(URLTest 类,跳过 GLOBAL 选择器),再对每组主动跑 /group//delay 取 + // 内核实测延迟。sing-box 的 /proxies 不带 history,故只能 active 取;成功就缓存,超时/空 + // 不覆盖旧值 → 显示稳定(congested 链路上探测时好时坏也不丢延迟)。 + clashGet("/proxies", query: nil) { [weak self] obj, raw in guard let self else { return } - let results = StatsCollector.extractUrltestResults(obj) let groups = StatsCollector.extractUrltestGroups(obj).filter { $0 != "GLOBAL" } self.queue.async { - if !results.isEmpty { - self.lock.lock() - self.latestUrltest = results - self.latest["urltestResults"] = results.map { ["tag": $0.tag, "delayMs": $0.delayMs] } - self.lock.unlock() - } - self.setDiag("hist=\(results.map { "\($0.tag):\($0.delayMs)" }.joined(separator: ","))") + if groups.isEmpty { self.setDiag("groups=[] raw=\(raw.prefix(100))") } for name in groups { self.clashGet("/group/\(name)/delay", query: ["url": "http://www.gstatic.com/generate_204", - "timeout": "5000"]) { _, _ in } + "timeout": "5000"]) { [weak self] dobj, draw in + guard let self else { return } + var urltest: [(tag: String, delayMs: Int)] = [] + if let dict = dobj as? [String: Any] { + for (tag, v) in dict { + if let d = (v as? NSNumber)?.intValue, d > 0 { + urltest.append((tag: tag, delayMs: d)) + } + } + } + self.queue.async { + self.lock.lock() + if !urltest.isEmpty { + self.latestUrltest = urltest + self.latest["urltestResults"] = urltest.map { ["tag": $0.tag, "delayMs": $0.delayMs] } + } + self.latest["diag"] = "d(\(name)) n=\(urltest.count) raw=\(draw.prefix(80))" + self.lock.unlock() + } + } } } } @@ -504,27 +517,6 @@ final class StatsCollector: NSObject { } return names } - - /// 从 /proxies 提取各 URLTest 组成员的最新 history 延迟(与 Windows extractUrltestResults 同法)。 - static func extractUrltestResults(_ obj: Any?) -> [(tag: String, delayMs: Int)] { - guard let root = obj as? [String: Any], - let proxies = root["proxies"] as? [String: Any] else { return [] } - var results: [(tag: String, delayMs: Int)] = [] - for (_, v) in proxies { - guard let group = v as? [String: Any], - let type = group["type"] as? String, type == "URLTest", - let members = group["all"] as? [Any] else { continue } - for m in members { - guard let tag = m as? String, - let member = proxies[tag] as? [String: Any], - let history = member["history"] as? [Any], - let last = history.last as? [String: Any], - let delay = (last["delay"] as? NSNumber)?.intValue, delay > 0 else { continue } - results.append((tag: tag, delayMs: delay)) - } - } - return results - } } extension StatsCollector: LibboxCommandClientHandlerProtocol { diff --git a/client/macos/Runner.xcodeproj/project.pbxproj b/client/macos/Runner.xcodeproj/project.pbxproj index b084235..fff4579 100644 --- a/client/macos/Runner.xcodeproj/project.pbxproj +++ b/client/macos/Runner.xcodeproj/project.pbxproj @@ -659,7 +659,7 @@ baseConfigurationReference = C57BBFD43F9175D1E23685EF /* Pods-RunnerTests.debug.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; - CURRENT_PROJECT_VERSION = 49; + CURRENT_PROJECT_VERSION = 50; GENERATE_INFOPLIST_FILE = YES; MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.pangolin.pangolin.RunnerTests; @@ -674,7 +674,7 @@ baseConfigurationReference = F8904897A48DC81799B8752E /* Pods-RunnerTests.release.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; - CURRENT_PROJECT_VERSION = 49; + CURRENT_PROJECT_VERSION = 50; GENERATE_INFOPLIST_FILE = YES; MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.pangolin.pangolin.RunnerTests; @@ -689,7 +689,7 @@ baseConfigurationReference = B21E68FC1F5D33DD67A0DF5E /* Pods-RunnerTests.profile.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; - CURRENT_PROJECT_VERSION = 49; + CURRENT_PROJECT_VERSION = 50; GENERATE_INFOPLIST_FILE = YES; MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.pangolin.pangolin.RunnerTests; @@ -960,7 +960,7 @@ CODE_SIGN_ENTITLEMENTS = PacketTunnel/PacketTunnel.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 49; + CURRENT_PROJECT_VERSION = 50; DEVELOPMENT_TEAM = BYL4KQHMTN; ENABLE_APP_SANDBOX = YES; ENABLE_HARDENED_RUNTIME = YES; @@ -1010,7 +1010,7 @@ CODE_SIGN_ENTITLEMENTS = PacketTunnel/PacketTunnel.entitlements; CODE_SIGN_IDENTITY = "Developer ID Application"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 49; + CURRENT_PROJECT_VERSION = 50; DEVELOPMENT_TEAM = BYL4KQHMTN; ENABLE_APP_SANDBOX = YES; ENABLE_HARDENED_RUNTIME = YES; @@ -1059,7 +1059,7 @@ CODE_SIGN_ENTITLEMENTS = PacketTunnel/PacketTunnel.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 49; + CURRENT_PROJECT_VERSION = 50; DEVELOPMENT_TEAM = BYL4KQHMTN; ENABLE_APP_SANDBOX = YES; ENABLE_HARDENED_RUNTIME = YES;