native macOS codings agent orchestrator prowl.onev.cat
Something went wrong. Try again.
Swift
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104import Foundation
actor GithubCLIExecutableResolver { nonisolated private static let logger = SupaLogger("GithubCLI")
private let fallbackExecutableURLs: [URL] private var cachedExecutableURL: URL? private var inFlightResolution: Task<URL, Error>?
init(fallbackExecutableURLs: [URL]) { self.fallbackExecutableURLs = fallbackExecutableURLs }
func executableURL(shell: ShellClient) async throws -> URL { if let cachedExecutableURL { return cachedExecutableURL } if let inFlightResolution { return try await inFlightResolution.value } let resolutionTask = Task { try await resolveExecutableURL(shell: shell) } inFlightResolution = resolutionTask do { let executableURL = try await resolutionTask.value cachedExecutableURL = executableURL inFlightResolution = nil return executableURL } catch { inFlightResolution = nil throw error } }
func invalidate() { cachedExecutableURL = nil inFlightResolution?.cancel() inFlightResolution = nil }
private func resolveExecutableURL(shell: ShellClient) async throws -> URL { if let executableURL = await locateExecutableURL( shell: shell, useLoginShell: false ) { return executableURL } if let executableURL = await locateExecutableURL( shell: shell, useLoginShell: true ) { return executableURL } if let executableURL = fallbackExecutableURLs.first(where: { FileManager.default.isExecutableFile(atPath: $0.path) }) { // Shell PATH missed gh; note the fixed-path fallback so a mismatch with the user's terminal is traceable. Self.logger.info("Resolved gh via fallback path \(executableURL.path); shell PATH resolution failed.") return executableURL } throw GithubCLIError.unavailable }
nonisolated static func defaultFallbackExecutableURLs( environment: [String: String] = ProcessInfo.processInfo.environment ) -> [URL] { [ "/opt/homebrew/bin/gh", "/usr/local/bin/gh", environment["HOME"].map { "\($0)/.local/bin/gh" }, ] .compactMap { $0 } .map { URL(fileURLWithPath: $0) } }
private func locateExecutableURL( shell: ShellClient, useLoginShell: Bool ) async -> URL? { let whichURL = URL(fileURLWithPath: "/usr/bin/which") do { let output: String if useLoginShell { output = try await shell.runLogin( whichURL, ["gh"], nil, log: false ).stdout } else { output = try await shell.run(whichURL, ["gh"], nil).stdout } let trimmed = output.trimmingCharacters(in: .whitespacesAndNewlines) guard !trimmed.isEmpty else { return nil } return URL(fileURLWithPath: trimmed) } catch { return nil } }}