diff --git a/edge-ai-platform/installer/app.go b/edge-ai-platform/installer/app.go index a4f5f2a..7eec41f 100644 --- a/edge-ai-platform/installer/app.go +++ b/edge-ai-platform/installer/app.go @@ -417,6 +417,7 @@ func (inst *Installer) installPython3Windows() error { }) cmd := exec.Command("winget", "install", "Python.Python.3.12", + "--source", "winget", "--accept-source-agreements", "--accept-package-agreements", "--silent") if out, err := cmd.CombinedOutput(); err != nil { return fmt.Errorf("winget install Python failed: %s — %w", string(out), err) @@ -495,20 +496,51 @@ func (inst *Installer) setupPythonVenv(installDir string) error { return fmt.Errorf("pip install failed: %s — %w", string(out), err) } - // Install KneronPLUS SDK wheel if bundled in payload + // Install KneronPLUS SDK wheel if bundled in payload. + // On macOS, prefer the macOS-specific wheel (v2.0.0 with .dylib); + // on Windows, prefer the Windows wheel (v3.1.2 with .dll). scriptsDir := filepath.Join(installDir, "scripts") - matches, _ := filepath.Glob(filepath.Join(scriptsDir, "KneronPLUS*.whl")) - if len(matches) > 0 { + var kpWheel string + if runtime.GOOS == "darwin" { + // macOS wheels are stored in scripts/macos/ subdirectory + macMatches, _ := filepath.Glob(filepath.Join(scriptsDir, "macos", "KneronPLUS*.whl")) + if len(macMatches) > 0 { + kpWheel = macMatches[0] + } + } + if kpWheel == "" { + // Fallback: pick any KneronPLUS wheel in scripts/ (Windows or generic) + matches, _ := filepath.Glob(filepath.Join(scriptsDir, "KneronPLUS*.whl")) + if len(matches) > 0 { + kpWheel = matches[0] + } + } + if kpWheel != "" { inst.emitProgress(ProgressEvent{ Step: "python", Message: "Installing Kneron SDK...", Percent: 84, }) - cmd = exec.Command(pipPath, "install", matches[0]) + cmd = exec.Command(pipPath, "install", kpWheel) if out, err := cmd.CombinedOutput(); err != nil { // Non-fatal: device detection may still work via pyusb _ = out } + + // On macOS, ad-hoc codesign the bundled dylibs so Gatekeeper allows loading + if runtime.GOOS == "darwin" { + sitePackages := filepath.Join(venvDir, "lib") + // Find the kp/lib directory inside the venv + _ = filepath.Walk(sitePackages, func(path string, info os.FileInfo, err error) error { + if err != nil { + return nil + } + if strings.HasSuffix(path, ".dylib") && strings.Contains(path, "kp/lib/") { + exec.Command("codesign", "--force", "--sign", "-", path).Run() + } + return nil + }) + } } inst.emitProgress(ProgressEvent{ diff --git a/edge-ai-platform/installer/platform_windows.go b/edge-ai-platform/installer/platform_windows.go index 4ba9282..7b8b343 100644 --- a/edge-ai-platform/installer/platform_windows.go +++ b/edge-ai-platform/installer/platform_windows.go @@ -195,6 +195,7 @@ func installFfmpeg(inst *Installer, needFfmpeg, needYtdlp bool) error { Percent: 79, }) cmd := exec.Command("winget", "install", "Gyan.FFmpeg", + "--source", "winget", "--accept-source-agreements", "--accept-package-agreements", "--silent") if out, err := cmd.CombinedOutput(); err != nil { return fmt.Errorf("winget install ffmpeg failed: %s — %w", string(out), err) @@ -220,6 +221,7 @@ func installFfmpeg(inst *Installer, needFfmpeg, needYtdlp bool) error { Percent: 80, }) cmd := exec.Command("winget", "install", "yt-dlp.yt-dlp", + "--source", "winget", "--accept-source-agreements", "--accept-package-agreements", "--silent") if out, err := cmd.CombinedOutput(); err != nil { // Non-fatal: yt-dlp is optional