native macOS codings agent orchestrator prowl.onev.cat
Something went wrong. Try again.
Swift
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155import Foundationimport GhosttyKitimport ProwlCLIShared
struct CLIWorktreeTerminalSnapshot: Sendable { let tabs: [CLITerminalTabSnapshot] let taskStatus: ListCommandTask.Status?}
struct CLITerminalTabSnapshot: Sendable { let id: UUID let handle: Int? let title: String let selected: Bool let focusedPaneID: UUID? let panes: [CLITerminalPaneSnapshot]}
struct CLITerminalPaneSnapshot: Sendable { let id: UUID let handle: Int? let title: String let cwd: String? let agent: String?}
extension WorktreeTerminalState { func makeCLIListSnapshot() -> CLIWorktreeTerminalSnapshot { let selectedTabID = tabManager.selectedTabId
let tabs: [CLITerminalTabSnapshot] = tabManager.tabs.map { tab in let paneIDs = trees[tab.id]?.leaves().map(\.id) ?? [] let panes = paneIDs.map { paneID in let cwd = inheritedSurfaceConfig( fromSurfaceId: paneID, context: GHOSTTY_SURFACE_CONTEXT_TAB ).workingDirectory?.path(percentEncoded: false)
let title = paneTitle(surfaceID: paneID, fallbackTabTitle: tab.displayTitle) let agent = surfaceAgentStates[paneID]?.detectedAgent?.rawValue return CLITerminalPaneSnapshot( id: paneID, handle: registerTargetHandle(for: paneID), title: title, cwd: cwd, agent: agent ) }
return CLITerminalTabSnapshot( id: tab.id.rawValue, handle: registerTargetHandle(for: tab.id), title: tab.displayTitle, selected: tab.id == selectedTabID, focusedPaneID: focusedSurfaceIdByTab[tab.id], panes: panes ) }
return CLIWorktreeTerminalSnapshot( tabs: tabs, taskStatus: taskStatus == .running ? .running : .idle ) }
func paneTitle(surfaceID: UUID, fallbackTabTitle: String) -> String { let rawTitle = surfaces[surfaceID]?.bridge.state.title?.trimmingCharacters( in: .whitespacesAndNewlines )
if let rawTitle, !rawTitle.isEmpty { return rawTitle }
return fallbackTabTitle }}
// MARK: - CLI Command Finished Waiting
extension WorktreeTerminalState { /// Returns an `AsyncStream` that yields exactly once when the command finishes /// on the given surface. The caller should race this against a timeout. func waitForCommandFinished(surfaceID: UUID) -> AsyncStream<(exitCode: Int?, durationMs: Int)> { // Cancel any existing waiter for this surface. commandFinishedWaiters[surfaceID]?.finish() commandFinishedWaiters.removeValue(forKey: surfaceID)
return AsyncStream { continuation in commandFinishedWaiters[surfaceID] = continuation continuation.onTermination = { [weak self] _ in Task { @MainActor in self?.commandFinishedWaiters.removeValue(forKey: surfaceID) } } } }}
// MARK: - CLI Send Snapshot
struct CLISendTabSnapshot { let focusedPaneID: UUID? let panes: [TargetResolutionSnapshot.Pane]}
extension WorktreeTerminalState { /// Shell child PIDs for every live pane, for CLI caller-pane resolution. func shellPIDsBySurface() -> [UUID: pid_t] { var pids: [UUID: pid_t] = [:] for (surfaceID, view) in surfaces { if let pid = view.bridge.childPID() { pids[surfaceID] = pid } } return pids }
func makeCLISendSnapshot(for tabId: TerminalTabID) -> CLISendTabSnapshot? { let paneIDs = trees[tabId]?.leaves().map(\.id) ?? [] guard !paneIDs.isEmpty else { return nil }
let focusedPaneID = focusedSurfaceIdByTab[tabId] let panes: [TargetResolutionSnapshot.Pane] = paneIDs.compactMap { paneID in guard let surfaceView = surfaces[paneID] else { return nil } let cwd = inheritedSurfaceConfig( fromSurfaceId: paneID, context: GHOSTTY_SURFACE_CONTEXT_TAB ).workingDirectory?.path(percentEncoded: false) let title = paneTitle(surfaceID: paneID, fallbackTabTitle: "") return TargetResolutionSnapshot.Pane( id: paneID, handle: registerTargetHandle(for: paneID), title: title, cwd: cwd, isFocusedInTab: paneID == focusedPaneID, surfaceView: surfaceView ) }
return CLISendTabSnapshot(focusedPaneID: focusedPaneID, panes: panes) }}
nonisolated func makeCommandInput(script: String) -> String? { let trimmed = script.trimmingCharacters(in: .whitespacesAndNewlines) guard !trimmed.isEmpty else { return nil } return trimmed + "\n"}
nonisolated func makeBlockingScriptInput(script: String) -> String? { guard let input = makeCommandInput(script: script) else { return nil } return input + "exit\n"}