package cc.unitmesh.agent.runconfig import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import java.util.concurrent.TimeUnit actual suspend fun readProcessOutputPlatform( processHandle: Any?, onOutput: (String) -> Unit, timeoutMs: Long, onProcessStarted: (processId: Int?) -> Unit ): RunConfigResult = withContext(Dispatchers.IO) { val process = processHandle as? Process ?: return@withContext RunConfigResult( success = false, error = "Invalid process handle" ) val startTime = System.currentTimeMillis() val outputBuilder = StringBuilder() var cancelled = false // Read stdout in a separate thread val stdoutReader = Thread { try { process.inputStream.bufferedReader().use { reader -> val buffer = CharArray(1024) while (!cancelled) { val charsRead = reader.read(buffer) if (charsRead == -1) break val chunk = buffer.concatToString(0, charsRead) outputBuilder.append(chunk) onOutput(chunk) } } } catch (_: Exception) { // Stream closed, ignore } }.apply { start() } // Read stderr in a separate thread val stderrReader = Thread { try { process.errorStream.bufferedReader().use { reader -> val buffer = CharArray(1024) while (!cancelled) { val charsRead = reader.read(buffer) if (charsRead == -1) break val chunk = buffer.concatToString(0, charsRead) outputBuilder.append(chunk) onOutput(chunk) } } } catch (_: Exception) { // Stream closed, ignore } }.apply { start() } try { // Wait for process with timeout, but don't kill on timeout val completed = process.waitFor(timeoutMs, TimeUnit.MILLISECONDS) // Wait for readers to finish (with short timeout) stdoutReader.join(2000) stderrReader.join(2000) if (!completed) { // Process still running after timeout - report but don't kill onOutput("\n[INFO] Process still running after ${timeoutMs / 1000}s timeout.\n") onProcessStarted(process.pid().toInt()) return@withContext RunConfigResult( success = true, message = "Process started (may still be running)", pid = process.pid().toInt() ) } val exitCode = process.exitValue() val executionTime = System.currentTimeMillis() - startTime RunConfigResult( success = exitCode == 0, exitCode = exitCode, message = if (exitCode == 0) { "Command completed successfully (${executionTime}ms)" } else { "Command exited with code $exitCode" } ) } catch (e: kotlinx.coroutines.CancellationException) { cancelled = true stdoutReader.interrupt() stderrReader.interrupt() throw e } } actual fun killProcessPlatform(processHandle: Any?) { val process = processHandle as? Process ?: return try { if (process.isAlive) { process.destroyForcibly() } } catch (_: Exception) { // Ignore } } actual fun isNativeProcess(handle: Any?): Boolean = handle is Process actual fun currentTimeMillis(): Long = System.currentTimeMillis()