| import Foundation |
|
|
| enum ToolResultTextFormatter { |
| static func format(text: String, toolName: String?) -> String { |
| let trimmed = text.trimmingCharacters(in: .whitespacesAndNewlines) |
| guard !trimmed.isEmpty else { return "" } |
|
|
| guard self.looksLikeJSON(trimmed), |
| let data = trimmed.data(using: .utf8), |
| let json = try? JSONSerialization.jsonObject(with: data) |
| else { |
| return trimmed |
| } |
|
|
| let normalizedTool = toolName?.trimmingCharacters(in: .whitespacesAndNewlines).lowercased() |
| return self.renderJSON(json, toolName: normalizedTool) |
| } |
|
|
| private static func looksLikeJSON(_ value: String) -> Bool { |
| guard let first = value.first else { return false } |
| return first == "{" || first == "[" |
| } |
|
|
| private static func renderJSON(_ json: Any, toolName: String?) -> String { |
| if let dict = json as? [String: Any] { |
| return self.renderDictionary(dict, toolName: toolName) |
| } |
| if let array = json as? [Any] { |
| if array.isEmpty { return "No items." } |
| return "\(array.count) item\(array.count == 1 ? "" : "s")." |
| } |
| return "" |
| } |
|
|
| private static func renderDictionary(_ dict: [String: Any], toolName: String?) -> String { |
| let status = (dict["status"] as? String)?.trimmingCharacters(in: .whitespacesAndNewlines) |
| let errorText = self.firstString(in: dict, keys: ["error", "reason"]) |
| let messageText = self.firstString(in: dict, keys: ["message", "result", "detail"]) |
|
|
| if status?.lowercased() == "error" || errorText != nil { |
| if let errorText { |
| return "Error: \(self.sanitizeError(errorText))" |
| } |
| if let messageText { |
| return "Error: \(self.sanitizeError(messageText))" |
| } |
| return "Error" |
| } |
|
|
| if toolName == "nodes", let summary = self.renderNodesSummary(dict) { |
| return summary |
| } |
|
|
| if let message = messageText { |
| return message |
| } |
|
|
| if let status, !status.isEmpty { |
| return "Status: \(status)" |
| } |
|
|
| return "" |
| } |
|
|
| private static func renderNodesSummary(_ dict: [String: Any]) -> String? { |
| if let nodes = dict["nodes"] as? [[String: Any]] { |
| if nodes.isEmpty { return "No nodes found." } |
| var lines: [String] = [] |
| lines.append("\(nodes.count) node\(nodes.count == 1 ? "" : "s") found.") |
|
|
| for node in nodes.prefix(3) { |
| let label = self.firstString(in: node, keys: ["displayName", "name", "nodeId"]) ?? "Node" |
| var details: [String] = [] |
|
|
| if let connected = node["connected"] as? Bool { |
| details.append(connected ? "connected" : "offline") |
| } |
| if let platform = self.firstString(in: node, keys: ["platform"]) { |
| details.append(platform) |
| } |
| if let version = self.firstString(in: node, keys: ["osVersion", "appVersion", "version"]) { |
| details.append(version) |
| } |
| if let pairing = self.pairingDetail(node) { |
| details.append(pairing) |
| } |
|
|
| if details.isEmpty { |
| lines.append("• \(label)") |
| } else { |
| lines.append("• \(label) - \(details.joined(separator: ", "))") |
| } |
| } |
|
|
| let extra = nodes.count - 3 |
| if extra > 0 { |
| lines.append("... +\(extra) more") |
| } |
| return lines.joined(separator: "\n") |
| } |
|
|
| if let pending = dict["pending"] as? [Any], let paired = dict["paired"] as? [Any] { |
| return "Pairing requests: \(pending.count) pending, \(paired.count) paired." |
| } |
|
|
| if let pending = dict["pending"] as? [Any] { |
| if pending.isEmpty { return "No pending pairing requests." } |
| return "\(pending.count) pending pairing request\(pending.count == 1 ? "" : "s")." |
| } |
|
|
| return nil |
| } |
|
|
| private static func pairingDetail(_ node: [String: Any]) -> String? { |
| if let paired = node["paired"] as? Bool, !paired { |
| return "pairing required" |
| } |
|
|
| for key in ["status", "state", "deviceStatus"] { |
| if let raw = node[key] as? String, raw.lowercased().contains("pairing required") { |
| return "pairing required" |
| } |
| } |
| return nil |
| } |
|
|
| private static func firstString(in dict: [String: Any], keys: [String]) -> String? { |
| for key in keys { |
| if let value = dict[key] as? String { |
| let trimmed = value.trimmingCharacters(in: .whitespacesAndNewlines) |
| if !trimmed.isEmpty { |
| return trimmed |
| } |
| } |
| } |
| return nil |
| } |
|
|
| private static func sanitizeError(_ raw: String) -> String { |
| var cleaned = raw.trimmingCharacters(in: .whitespacesAndNewlines) |
| if cleaned.contains("agent="), |
| cleaned.contains("action="), |
| let marker = cleaned.range(of: ": ") |
| { |
| cleaned = String(cleaned[marker.upperBound...]).trimmingCharacters(in: .whitespacesAndNewlines) |
| } |
|
|
| if let firstLine = cleaned.split(separator: "\n").first { |
| cleaned = String(firstLine).trimmingCharacters(in: .whitespacesAndNewlines) |
| } |
|
|
| if cleaned.count > 220 { |
| cleaned = String(cleaned.prefix(217)) + "..." |
| } |
| return cleaned |
| } |
| } |
|
|