Files
openclaw/apps/macos/Sources/OpenClaw/NodeMode/MacNodeFileSystemCommands.swift
2026-07-12 02:41:17 -07:00

104 lines
3.8 KiB
Swift

import Foundation
import OpenClawKit
enum MacNodeFileSystemCommands {
private struct ListDirectoryParams: Decodable {
var path: String?
}
private struct DirectoryEntry: Encodable {
var name: String
var path: String
var hidden: Bool?
}
private struct ListDirectoryPayload: Encodable {
var path: String
var parent: String?
var home: String
var entries: [DirectoryEntry]
}
static func listDirectory(_ request: BridgeInvokeRequest) throws -> BridgeInvokeResponse {
let params: ListDirectoryParams = if let paramsJSON = request.paramsJSON {
try self.decodeParams(paramsJSON)
} else {
ListDirectoryParams(path: nil)
}
let home = FileManager.default.homeDirectoryForCurrentUser.path
let requested = params.path?.trimmingCharacters(in: .whitespacesAndNewlines)
let rawPath = requested.flatMap { $0.isEmpty ? nil : $0 } ?? home
guard NSString(string: rawPath).isAbsolutePath else {
return self.errorResponse(
request,
code: .invalidRequest,
message: "INVALID_REQUEST: fs.listDir path must be absolute")
}
let directory = URL(fileURLWithPath: rawPath, isDirectory: true).standardizedFileURL
let children = try FileManager.default.contentsOfDirectory(
at: directory,
includingPropertiesForKeys: [.isDirectoryKey],
options: [])
var entries: [DirectoryEntry] = []
for child in children {
var isDirectory: ObjCBool = false
guard FileManager.default.fileExists(atPath: child.path, isDirectory: &isDirectory),
isDirectory.boolValue
else { continue }
let name = child.lastPathComponent
entries.append(DirectoryEntry(
name: name,
path: directory.appendingPathComponent(name, isDirectory: true).path,
hidden: name.hasPrefix(".") ? true : nil))
}
entries.sort { lhs, rhs in
if (lhs.hidden != nil) != (rhs.hidden != nil) {
return lhs.hidden == nil
}
return lhs.name.utf8.lexicographicallyPrecedes(rhs.name.utf8)
}
let parentPath = directory.deletingLastPathComponent().path
let payload = ListDirectoryPayload(
path: directory.path,
parent: parentPath == directory.path ? nil : parentPath,
home: home,
entries: entries)
return try BridgeInvokeResponse(
id: request.id,
ok: true,
payloadJSON: self.encodePayload(payload))
}
private static func decodeParams(_ paramsJSON: String) throws -> ListDirectoryParams {
guard let data = paramsJSON.data(using: .utf8) else {
throw NSError(domain: "Gateway", code: 20, userInfo: [
NSLocalizedDescriptionKey: "INVALID_REQUEST: paramsJSON required",
])
}
return try JSONDecoder().decode(ListDirectoryParams.self, from: data)
}
private static func encodePayload(_ payload: ListDirectoryPayload) throws -> String {
let data = try JSONEncoder().encode(payload)
guard let json = String(bytes: data, encoding: .utf8) else {
throw NSError(domain: "Node", code: 21, userInfo: [
NSLocalizedDescriptionKey: "Failed to encode payload as UTF-8",
])
}
return json
}
private static func errorResponse(
_ request: BridgeInvokeRequest,
code: OpenClawNodeErrorCode,
message: String) -> BridgeInvokeResponse
{
BridgeInvokeResponse(
id: request.id,
ok: false,
error: OpenClawNodeError(code: code, message: message))
}
}