c118996746
- SwiftUI macOS app with C++17 code analysis engine (ObjC++ bridge) - Agentic AI loop: LLM plans → tool calls → execution → feedback loop - 15 agent tools: file ops, terminal, git, xcode build, code intel - 7 persistent terminal tools with background session management - Chat sidebar with agent step rendering and auto-apply - NVIDIA NIM API integration (Llama 3.3 70B default) - OpenAI tool_calls format with prompt-based fallback - Code editor with syntax highlighting and multi-tab support - File tree, console view, terminal view - Git integration and workspace management
134 lines
4.5 KiB
Swift
134 lines
4.5 KiB
Swift
import Foundation
|
|
|
|
struct ExecutionResult: Sendable {
|
|
let stdout: String
|
|
let stderr: String
|
|
let exitCode: Int32
|
|
let duration: TimeInterval
|
|
|
|
var succeeded: Bool { exitCode == 0 }
|
|
|
|
var formattedOutput: String {
|
|
var parts: [String] = []
|
|
if !stdout.isEmpty { parts.append(stdout) }
|
|
if !stderr.isEmpty { parts.append(stderr) }
|
|
parts.append("Exit code: \(exitCode) (\(String(format: "%.2fs", duration)))")
|
|
return parts.joined(separator: "\n")
|
|
}
|
|
}
|
|
|
|
enum CodeExecutionError: Error, LocalizedError {
|
|
case notSupportedPlatform
|
|
case failedToLaunch(String)
|
|
case timeout
|
|
case cancelled
|
|
|
|
var errorDescription: String? {
|
|
switch self {
|
|
case .notSupportedPlatform:
|
|
return "Code execution is not supported on this platform."
|
|
case .failedToLaunch(let reason):
|
|
return "Failed to launch process: \(reason)"
|
|
case .timeout:
|
|
return "Execution timed out."
|
|
case .cancelled:
|
|
return "Execution was cancelled."
|
|
}
|
|
}
|
|
}
|
|
|
|
protocol CodeEngine: AnyObject {
|
|
func run(_ source: String) async throws -> ExecutionResult
|
|
func cancel()
|
|
}
|
|
|
|
final class SwiftCodeEngine: CodeEngine, @unchecked Sendable {
|
|
private var currentProcess: Process?
|
|
private let lock = NSLock()
|
|
private let timeoutInterval: TimeInterval
|
|
|
|
init(timeoutInterval: TimeInterval = 30) {
|
|
self.timeoutInterval = timeoutInterval
|
|
}
|
|
|
|
func run(_ source: String) async throws -> ExecutionResult {
|
|
#if os(macOS)
|
|
let startTime = CFAbsoluteTimeGetCurrent()
|
|
|
|
return try await withCheckedThrowingContinuation { continuation in
|
|
DispatchQueue.global(qos: .userInitiated).async { [weak self] in
|
|
guard let self else {
|
|
continuation.resume(throwing: CodeExecutionError.cancelled)
|
|
return
|
|
}
|
|
|
|
let tempDir = FileManager.default.temporaryDirectory
|
|
let fileURL = tempDir.appendingPathComponent("CxIDE-\(UUID().uuidString).swift")
|
|
|
|
do {
|
|
try source.write(to: fileURL, atomically: true, encoding: .utf8)
|
|
defer { try? FileManager.default.removeItem(at: fileURL) }
|
|
|
|
let process = Process()
|
|
process.executableURL = URL(fileURLWithPath: "/usr/bin/xcrun")
|
|
process.arguments = ["swift", fileURL.path]
|
|
process.currentDirectoryURL = tempDir
|
|
|
|
let stdoutPipe = Pipe()
|
|
let stderrPipe = Pipe()
|
|
process.standardOutput = stdoutPipe
|
|
process.standardError = stderrPipe
|
|
|
|
self.lock.lock()
|
|
self.currentProcess = process
|
|
self.lock.unlock()
|
|
|
|
try process.run()
|
|
|
|
// Timeout handling
|
|
let timer = DispatchSource.makeTimerSource(queue: .global())
|
|
timer.schedule(deadline: .now() + self.timeoutInterval)
|
|
timer.setEventHandler {
|
|
process.terminate()
|
|
}
|
|
timer.resume()
|
|
|
|
process.waitUntilExit()
|
|
timer.cancel()
|
|
|
|
self.lock.lock()
|
|
self.currentProcess = nil
|
|
self.lock.unlock()
|
|
|
|
let outData = stdoutPipe.fileHandleForReading.readDataToEndOfFile()
|
|
let errData = stderrPipe.fileHandleForReading.readDataToEndOfFile()
|
|
|
|
let stdout = String(data: outData, encoding: .utf8) ?? ""
|
|
let stderr = String(data: errData, encoding: .utf8) ?? ""
|
|
let duration = CFAbsoluteTimeGetCurrent() - startTime
|
|
|
|
let result = ExecutionResult(
|
|
stdout: stdout,
|
|
stderr: stderr,
|
|
exitCode: process.terminationStatus,
|
|
duration: duration
|
|
)
|
|
continuation.resume(returning: result)
|
|
} catch {
|
|
continuation.resume(throwing: CodeExecutionError.failedToLaunch(error.localizedDescription))
|
|
}
|
|
}
|
|
}
|
|
#else
|
|
throw CodeExecutionError.notSupportedPlatform
|
|
#endif
|
|
}
|
|
|
|
func cancel() {
|
|
lock.lock()
|
|
currentProcess?.terminate()
|
|
currentProcess = nil
|
|
lock.unlock()
|
|
}
|
|
}
|