import Foundation // Bridge to invoke Prole Tools environment wrapper for status/restart. // New requirement: always use "$PROLE_HOME/.prole/env.sh init-port-forwards " // rather than calling scripts directly, to ensure the correct environment. enum PFScriptBridge { /// Resolve the env wrapper URL: "$PROLE_HOME/env.sh" /// Defaults PROLE_HOME to "$HOME" if not present. private static func resolveEnvURL() -> URL { let env = ProcessInfo.processInfo.environment let home = env["HOME"] ?? NSHomeDirectory() let proleHome = env["PROLE_HOME"] ?? home return URL(fileURLWithPath: "\(proleHome)/env.sh") } /// Public accessor for display purposes. Shows the invocation we use. static func invocationDescription() -> String { "env.sh init-port-forwards" } @discardableResult static func restart() -> (code: Int32, out: String, err: String) { runScript(arg: "restart") } static func status() -> (code: Int32, out: String, err: String) { runScript(arg: "status") } private static func runScript(arg: String) -> (code: Int32, out: String, err: String) { let p = Process() let envURL = resolveEnvURL() // Execute via bash -lc to export PROLE_HOME default and run env.sh subcommand p.executableURL = URL(fileURLWithPath: "/bin/bash") // Compose command: PROLE_HOME default to $HOME, then "$PROLE_HOME/env.sh init-port-forwards " let cmd = "export PROLE_HOME=\"${PROLE_HOME:-$HOME}\"; \"\(envURL.path)\" init-port-forwards \(arg)" Logger.shared.info("$PROLE_HOME/env.sh init-port-forwards \(arg)") p.arguments = ["-lc", cmd] // Working directory not important; use user's home to avoid sandbox path surprises p.currentDirectoryURL = URL(fileURLWithPath: NSHomeDirectory()) let outPipe = Pipe(); let errPipe = Pipe() p.standardOutput = outPipe p.standardError = errPipe do { try p.run() } catch { let errStr = String(describing: error) Logger.shared.error("env.sh run error: \(errStr)") return (-1, "", errStr) } p.waitUntilExit() let out = String(data: outPipe.fileHandleForReading.readDataToEndOfFile(), encoding: .utf8) ?? "" let err = String(data: errPipe.fileHandleForReading.readDataToEndOfFile(), encoding: .utf8) ?? "" let code = p.terminationStatus if !out.isEmpty { Logger.shared.info("stdout:\n\(out)") } if !err.isEmpty { Logger.shared.error("stderr:\n\(err)") } Logger.shared.info("exit code: \(code)") return (code, out, err) } }