mirror of
https://github.com/moltbot/moltbot.git
synced 2026-03-08 06:54:24 +00:00
Merged via /review-pr -> /prepare-pr -> /merge-pr.
Prepared head SHA: 34dd87b0c0
Co-authored-by: mbelinky <132747814+mbelinky@users.noreply.github.com>
Co-authored-by: mbelinky <132747814+mbelinky@users.noreply.github.com>
Reviewed-by: @mbelinky
158 lines
5.6 KiB
Swift
158 lines
5.6 KiB
Swift
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
|
|
}
|
|
}
|