mirror of
https://github.com/hansjone/oclaw.git
synced 2026-10-10 17:23:25 +08:00
重构主控编排与运行时预热链路,统一工作区提示词/专家调度协议并补齐 wiki 记忆注入与写回闭环。
同时收敛启动与运维脚本默认行为(含 wiki worker)、更新 Admin 可观测性与相关测试,降低首轮时延并提高运行稳定性。 Made-with: Cursor
This commit is contained in:
parent
4a23b715a2
commit
dbbe3add6a
14438 changed files with 2693620 additions and 2546 deletions
147
openclaw/apps/macos/Sources/OpenClaw/ShellExecutor.swift
Normal file
147
openclaw/apps/macos/Sources/OpenClaw/ShellExecutor.swift
Normal file
|
|
@ -0,0 +1,147 @@
|
|||
import Foundation
|
||||
import OpenClawIPC
|
||||
|
||||
enum ShellExecutor {
|
||||
struct ShellResult {
|
||||
var stdout: String
|
||||
var stderr: String
|
||||
var exitCode: Int?
|
||||
var timedOut: Bool
|
||||
var success: Bool
|
||||
var errorMessage: String?
|
||||
}
|
||||
|
||||
private final class CompletionBox: @unchecked Sendable {
|
||||
private let lock = NSLock()
|
||||
private var finished = false
|
||||
private let continuation: CheckedContinuation<ShellResult, Never>
|
||||
|
||||
init(continuation: CheckedContinuation<ShellResult, Never>) {
|
||||
self.continuation = continuation
|
||||
}
|
||||
|
||||
func finish(_ result: ShellResult) {
|
||||
self.lock.lock()
|
||||
defer { self.lock.unlock() }
|
||||
guard !self.finished else { return }
|
||||
self.finished = true
|
||||
self.continuation.resume(returning: result)
|
||||
}
|
||||
}
|
||||
|
||||
private static func completedResult(
|
||||
status: Int,
|
||||
outTask: Task<Data, Never>,
|
||||
errTask: Task<Data, Never>) async -> ShellResult
|
||||
{
|
||||
let out = await outTask.value
|
||||
let err = await errTask.value
|
||||
return ShellResult(
|
||||
stdout: String(bytes: out, encoding: .utf8) ?? "",
|
||||
stderr: String(bytes: err, encoding: .utf8) ?? "",
|
||||
exitCode: status,
|
||||
timedOut: false,
|
||||
success: status == 0,
|
||||
errorMessage: status == 0 ? nil : "exit \(status)")
|
||||
}
|
||||
|
||||
static func runDetailed(
|
||||
command: [String],
|
||||
cwd: String?,
|
||||
env: [String: String]?,
|
||||
timeout: Double?) async -> ShellResult
|
||||
{
|
||||
guard !command.isEmpty else {
|
||||
return ShellResult(
|
||||
stdout: "",
|
||||
stderr: "",
|
||||
exitCode: nil,
|
||||
timedOut: false,
|
||||
success: false,
|
||||
errorMessage: "empty command")
|
||||
}
|
||||
|
||||
let process = Process()
|
||||
process.executableURL = URL(fileURLWithPath: "/usr/bin/env")
|
||||
process.arguments = command
|
||||
if let cwd { process.currentDirectoryURL = URL(fileURLWithPath: cwd) }
|
||||
if let env { process.environment = env }
|
||||
|
||||
let stdoutPipe = Pipe()
|
||||
let stderrPipe = Pipe()
|
||||
process.standardOutput = stdoutPipe
|
||||
process.standardError = stderrPipe
|
||||
|
||||
let outTask = Task { stdoutPipe.fileHandleForReading.readToEndSafely() }
|
||||
let errTask = Task { stderrPipe.fileHandleForReading.readToEndSafely() }
|
||||
|
||||
if let timeout, timeout > 0 {
|
||||
return await withCheckedContinuation { continuation in
|
||||
let completion = CompletionBox(continuation: continuation)
|
||||
|
||||
process.terminationHandler = { terminatedProcess in
|
||||
let status = Int(terminatedProcess.terminationStatus)
|
||||
Task {
|
||||
let result = await self.completedResult(
|
||||
status: status,
|
||||
outTask: outTask,
|
||||
errTask: errTask)
|
||||
completion.finish(result)
|
||||
}
|
||||
}
|
||||
|
||||
do {
|
||||
try process.run()
|
||||
} catch {
|
||||
completion.finish(
|
||||
ShellResult(
|
||||
stdout: "",
|
||||
stderr: "",
|
||||
exitCode: nil,
|
||||
timedOut: false,
|
||||
success: false,
|
||||
errorMessage: "failed to start: \(error.localizedDescription)"))
|
||||
return
|
||||
}
|
||||
|
||||
DispatchQueue.global(qos: .userInitiated).asyncAfter(deadline: .now() + timeout) {
|
||||
guard process.isRunning else { return }
|
||||
process.terminate()
|
||||
completion.finish(
|
||||
ShellResult(
|
||||
stdout: "",
|
||||
stderr: "",
|
||||
exitCode: nil,
|
||||
timedOut: true,
|
||||
success: false,
|
||||
errorMessage: "timeout"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
do {
|
||||
try process.run()
|
||||
} catch {
|
||||
return ShellResult(
|
||||
stdout: "",
|
||||
stderr: "",
|
||||
exitCode: nil,
|
||||
timedOut: false,
|
||||
success: false,
|
||||
errorMessage: "failed to start: \(error.localizedDescription)")
|
||||
}
|
||||
|
||||
process.waitUntilExit()
|
||||
return await self.completedResult(
|
||||
status: Int(process.terminationStatus),
|
||||
outTask: outTask,
|
||||
errTask: errTask)
|
||||
}
|
||||
|
||||
static func run(command: [String], cwd: String?, env: [String: String]?, timeout: Double?) async -> Response {
|
||||
let result = await self.runDetailed(command: command, cwd: cwd, env: env, timeout: timeout)
|
||||
let combined = result.stdout.isEmpty ? result.stderr : result.stdout
|
||||
let payload = combined.isEmpty ? nil : Data(combined.utf8)
|
||||
return Response(ok: result.success, message: result.errorMessage, payload: payload)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue