diff --git a/src/main/kotlin/Main.kt b/src/main/kotlin/Main.kt index 23d24ac..9d5ea7b 100644 --- a/src/main/kotlin/Main.kt +++ b/src/main/kotlin/Main.kt @@ -65,6 +65,7 @@ import service.TradingDecisionCallback import ui.SettingsScreen import ui.TradingDecisionLog import util.PortFinder +import java.io.File // 화면 상태 정의 enum class AppScreen { Settings, Dashboard, TradingDecision } @@ -158,8 +159,47 @@ fun main() = application { } ) + /** + * Mac OS 전용: '확인되지 않은 개발자' 보안(Gatekeeper) 해제 및 실행 권한 부여 + * @param targetPath 권한을 풀 파일 또는 폴더의 절대 경로 + */ + fun unlockMacPermissions(targetPath: String) { + val os = System.getProperty("os.name").lowercase() + if (!os.contains("mac")) return // Mac 환경이 아니면 조용히 패스 + + val targetFile = File(targetPath) + if (!targetFile.exists()) { + println("⚠️ [System] 권한을 부여할 경로를 찾을 수 없습니다: $targetPath") + return + } + + try { + println("🔓 [System] Mac 보안 권한 해제 시도 중... ($targetPath)") + + // 1. Apple Quarantine(격리) 속성 제거 (가장 중요!) + // 악성코드 방지 시스템(Gatekeeper)이 dylib 파일들을 차단하는 것을 막습니다. + val xattrProcess = ProcessBuilder("xattr", "-cr", targetPath) + .redirectErrorStream(true) + .start() + xattrProcess.waitFor() + + // 2. 실행 권한 부여 (chmod -R 777) + // -R 옵션으로 하위의 모든 라이브러리 파일까지 권한을 엽니다. + val chmodProcess = ProcessBuilder("chmod", "-R", "777", targetPath) + .redirectErrorStream(true) + .start() + chmodProcess.waitFor() + + println("✅ [System] Mac 권한 해제 및 실행 권한 부여 완료!") + + } catch (e: Exception) { + println("❌ [System] Mac 권한 부여 실패: ${e.message}") + } + } + // 앱 실행 시 필요한 바이너리 경로 (실행 파일 위치) val binPath = getLlamaBinPath() + unlockMacPermissions(binPath) val windowState = rememberWindowState( placement = WindowPlacement.Floating )