9fd04b0ce4
Add the owner-only AF_UNIX Reyna CLI privacy host, strict signed-app installation, and typed native routing for Calendar, Contacts, and Reminders.\n\nAdd bounded system-status paths and config-only direct local-service wrappers. Preserve MacMiniMCP pending explicit cutover approval.\n\nApple Notes is intentionally deferred: no native Notes operations, Apple Events declaration, or Automation helper are included; legacy Notes handling remains untouched.
76 lines
2.8 KiB
Swift
76 lines
2.8 KiB
Swift
import Foundation
|
|
|
|
// Public entry point used by both SwiftPM executable and Xcode app target.
|
|
// Preserves AF_UNIX privacy-host protocol exactly as before.
|
|
// Headless design: socket-server or stdin JSON-lines mode only.
|
|
|
|
public func runReynaCLIHost(arguments: [String] = CommandLine.arguments) -> Never {
|
|
if let idx = arguments.firstIndex(of: "--socket") {
|
|
let nextIdx = idx + 1
|
|
guard nextIdx < arguments.count else {
|
|
fputs("error: --socket requires a path argument\n", stderr)
|
|
Darwin.exit(2)
|
|
}
|
|
let socketPath = arguments[nextIdx]
|
|
runSocketServer(socketPath: socketPath)
|
|
} else {
|
|
runStdinLoop()
|
|
}
|
|
}
|
|
|
|
func extractRecoverableId(from data: Data) -> String? {
|
|
if let obj = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any],
|
|
let id = obj["id"] as? String {
|
|
return id
|
|
}
|
|
guard let str = String(data: data, encoding: .utf8) else { return nil }
|
|
let pattern = "\"id\"\\s*:\\s*\"([^\"]*)\""
|
|
guard let regex = try? NSRegularExpression(pattern: pattern, options: []),
|
|
let match = regex.firstMatch(in: str, options: [], range: NSRange(str.startIndex..., in: str)),
|
|
match.numberOfRanges >= 2,
|
|
let r = Range(match.range(at: 1), in: str) else {
|
|
return nil
|
|
}
|
|
return String(str[r])
|
|
}
|
|
|
|
func writeResponse(_ response: Response) {
|
|
guard let jsonData = try? JSONEncoder().encode(response),
|
|
let jsonString = String(data: jsonData, encoding: .utf8),
|
|
let outData = (jsonString + "\n").data(using: .utf8) else {
|
|
return
|
|
}
|
|
FileHandle.standardOutput.write(outData)
|
|
}
|
|
|
|
func handleLine(_ lineData: Data) {
|
|
if lineData.isEmpty { return }
|
|
if let s = String(data: lineData, encoding: .utf8),
|
|
s.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
|
|
return
|
|
}
|
|
if let request = try? JSONDecoder().decode(Request.self, from: lineData) {
|
|
let response = dispatch(request: request)
|
|
writeResponse(response)
|
|
} else {
|
|
let recoveredId = extractRecoverableId(from: lineData)
|
|
let err = ErrorPayload(code: "invalid_request", message: "Invalid request JSON")
|
|
let resp = Response(id: recoveredId ?? "", ok: false, result: nil, error: err)
|
|
writeResponse(resp)
|
|
}
|
|
}
|
|
|
|
func runStdinLoop() -> Never {
|
|
while let line = readLine() {
|
|
if line.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty { continue }
|
|
if let data = line.data(using: .utf8) {
|
|
handleLine(data)
|
|
} else {
|
|
let err = ErrorPayload(code: "invalid_request", message: "Invalid request encoding")
|
|
let resp = Response(id: "", ok: false, result: nil, error: err)
|
|
writeResponse(resp)
|
|
}
|
|
}
|
|
Darwin.exit(0)
|
|
}
|