Update PRD v3.2 + TDD v2.1: WinUSB auto-install, ffmpeg, uninstall fixes

PRD v3.2:
- Document WinUSB driver auto-install (self-signed cert + pnputil)
- ffmpeg/yt-dlp now mandatory install components
- Update uninstall description (Wails native dialog, preserve system deps)
- Fix packaging output description (DMG + ad-hoc codesign)

TDD v2.1:
- Rewrite install steps to match actual 11-step implementation
- Add detailed Windows WinUSB driver install flow (self-sign + pnputil)
- Update uninstall flow with ConfirmUninstall native dialog
- Update ProgressEvent struct to match actual implementation

Installer:
- Add ConfirmUninstall() with Wails native MessageDialog
- Replace JS confirm() with Go native dialog (fixes macOS WebView issue)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
jim800121chen 2026-03-10 02:10:28 +08:00
parent 98b89fe104
commit 7b4492c41e
4 changed files with 108 additions and 50 deletions

View File

@ -8,8 +8,8 @@
|------|------|
| 文件名稱 | 邊緣 AI 開發平台 PRD |
| 產品名稱 | (暫未定名,以下稱「本平台」) |
| 版本 | v3.1 |
| 日期 | 2026-03-05 |
| 版本 | v3.2 |
| 日期 | 2026-03-10 |
| 狀態 | 更新中 |
---
@ -1225,16 +1225,16 @@ Kneron Dongle Arduino 開發板 非 Kneron 晶片
| **Relay 設定步驟** | 新增 Relay Configuration 步驟:輸入 Relay URL、Relay Token、本地 Port預設 3721設定儲存至共用設定檔供 Launcher 與 server 讀取 |
| **i18n 支援** | 安裝程式支援多語系切換English + 繁體中文),安裝介面右上角提供語言切換器,所有步驟文字與錯誤訊息皆翻譯 |
| **設定檔** | `~/.edge-ai-platform/config.json`,安裝程式與 Launcher系統匣應用共用同一設定檔包含 relay URL/token、port、語言偏好、auto-start 設定等 |
| **必要元件(自動安裝)** | edge-ai-server binary含嵌入式前端、Python 3 venv + numpy + opencv-python-headless + pyusb、Kneron 韌體檔案KL520 + KL720、NEF 預訓練模型、libusbUSB 裝置通訊) |
| **可選元件** | ffmpeg攝影機/影片串流、yt-dlpYouTube 影片下載) |
| **自動依賴解析** | macOS: 自動安裝 Homebrew若未安裝`brew install libusb python3 ffmpeg`Windows: 自動下載 Python embedded + libusb DLL免管理員權限 |
| **必要元件(自動安裝)** | edge-ai-server binary含嵌入式前端、Python 3 venv + numpy + opencv-python-headless + pyusb、Kneron 韌體檔案KL520 + KL720、NEF 預訓練模型、libusbUSB 裝置通訊)、WinUSB 驅動程式Windows自動自簽安裝、ffmpeg + yt-dlp媒體工具 |
| **自動依賴解析** | macOS: 自動安裝 Homebrew若未安裝`brew install libusb python3 ffmpeg yt-dlp`Windows: 自動下載 Python embedded + libusb DLL + WinUSB driver 自簽安裝 + winget 安裝 ffmpeg/yt-dlp |
| **Windows WinUSB 驅動自動安裝** | 安裝程式自動建立自簽 code signing 憑證PowerShell `New-SelfSignedCertificate`)→ 安裝至 TrustedPublisher + Root 憑證存儲區 → `pnputil /add-driver /install` 安裝 WinUSB 驅動,支援 KL520VID_3231&PID_0100+ KL720PID_0200, PID_0720無需使用者手動操作 Zadig |
| **即時進度顯示** | 每個安裝步驟獨立顯示進度條 + 狀態文字(下載中 → 解壓中 → 設定中 → 完成),失敗時顯示錯誤訊息 + 重試按鈕 |
| **硬體偵測** | 安裝完成後自動掃描 USB Kneron 裝置顯示偵測到的晶片型號KL520/KL720、韌體版本、連線狀態KL720 KDP legacy 裝置提示一鍵韌體更新 |
| **解除安裝** | 內建解除安裝功能:刪除 server binary + Python venv + 資料檔案 + symlink/PATHmacOS 提供拖曳到垃圾桶 + 深度清理選項Windows 整合「新增或移除程式」 |
| **解除安裝** | 內建解除安裝功能Wails 原生確認對話框)停止 server 進程 → 移除 auto-restart 服務 → 刪除 server binary + Python venv + 資料檔案 + scripts + config/logs + symlink/PATH系統依賴Python、libusb、Homebrew保留不動 |
| **安裝目錄** | macOS: `~/.edge-ai-platform/`(不需 sudo、Windows: `%LOCALAPPDATA%\EdgeAIPlatform\`(不需管理員) |
| **安裝完成動作** | 自動啟動選項現在註冊 Launcher系統匣應用程式而非裸 server 進程;可選擇立即啟動 Launcher + 自動開啟瀏覽器(`http://localhost:3721`)、建立桌面捷徑、設定開機自動啟動 Launcher |
| **更新支援** | 偵測現有安裝版本,僅更新 binary + 新模型保留使用者資料custom-models、設定檔與 Python venv |
| **打包產出** | macOS: `EdgeAI-Installer.dmg`(含 .app + 背景圖 + Applications 捷徑、Windows: `EdgeAI-Setup.exe`NSIS + Wails 包裝 |
| **打包產出** | macOS: `EdgeAI-Installer.dmg`(含 .appad-hoc codesign + xattr quarantine 清除、Windows: `EdgeAI-Installer.exe`Wails 單一執行檔 |
| **安裝體積** | 完整安裝約 300-400 MBbinary ~10MB + 模型 ~73MB + Python venv ~250MB + firmware ~2MB |
| **UI 設計** | 現代化視覺設計,包含步驟進度指示器、平滑過渡動畫、統一的色彩主題與字體排版 |

View File

@ -7,9 +7,9 @@
| 項目 | 內容 |
|------|------|
| 文件名稱 | 邊緣 AI 開發平台 TDD |
| 對應 PRD | PRD-Integrated.md v3.1 |
| 版本 | v2.0 |
| 日期 | 2026-03-05 |
| 對應 PRD | PRD-Integrated.md v3.2 |
| 版本 | v2.1 |
| 日期 | 2026-03-10 |
| 狀態 | 更新中 |
---
@ -1919,15 +1919,17 @@ func (a *Installer) LaunchServer() error // 啟動 edge-ai-server
func (a *Installer) OpenBrowser() error // 開啟瀏覽器
// 解除安裝 API
func (a *Installer) GetInstalledInfo() InstalledInfo // 讀取已安裝版本 + 大小
func (a *Installer) Uninstall(keepData bool) error // 解除安裝(可選保留資料
func (a *Installer) ConfirmUninstall() (bool, error) // Wails 原生確認對話框
func (a *Installer) Uninstall() error // 解除安裝(非同步 goroutine
// 進度回報(透過 Wails Events 推送到前端)
type StepProgress struct {
Step string `json:"step"` // "download", "extract", "python", "libusb", ...
Percent int `json:"percent"` // 0-100
Message string `json:"message"` // 人類可讀狀態
Error string `json:"error"` // 錯誤訊息(空=正常)
// 安裝: "install:progress"、解除安裝: "uninstall:progress"
type ProgressEvent struct {
Step string `json:"step"` // "extract", "python", "libusb", "driver", "ffmpeg", ...
Message string `json:"message"` // 人類可讀狀態
Percent float64 `json:"percent"` // 0-100
IsError bool `json:"isError"` // 是否為錯誤
IsComplete bool `json:"isComplete"` // 是否完成
}
```
@ -2061,19 +2063,47 @@ type StepProgress struct {
**安裝步驟實作細節:**
| 步驟 | macOS 實作 | Windows 實作 |
|------|-----------|-------------|
| 下載 binary | `net/http` GET → Gitea release tar.gztray-enabled build | `net/http` GET → Gitea release ziptray-enabled build |
| 解壓 | `archive/tar` + `compress/gzip` | `archive/zip` |
| symlink | `os.Symlink()``/usr/local/bin/edge-ai-server` (需確認權限) | 加入 User PATH (`os.Setenv` + registry) |
| libusb | `exec.Command("brew", "install", "libusb")` 或內嵌 dylib | 內嵌 `libusb-1.0.dll` 到安裝目錄 |
| Python venv | `exec.Command("python3", "-m", "venv", ...)` | `exec.Command("python", "-m", "venv", ...)` 或內嵌 Python embedded |
| pip install | `venv/bin/pip install numpy opencv-python-headless pyusb` | `venv\Scripts\pip install ...` |
| ffmpeg | `brew install ffmpeg` (可選) | `winget install Gyan.FFmpeg` 或內嵌 (可選) |
| 寫入設定檔 | 寫入 `~/.edge-ai-platform/config.json`(含 port、relay URL/token、語言偏好 | 寫入 `%LOCALAPPDATA%\EdgeAIPlatform\config.json` |
| 硬體偵測 | `exec.Command(python, "kneron_detect.py")` | 同左 |
| 桌面捷徑 | `~/Desktop/EdgeAI.command` | `shell:desktop\EdgeAI.lnk` (COM API) |
| 開機啟動 | `~/Library/LaunchAgents/com.innovedus.edge-ai.plist`(執行 `edge-ai-server --tray` | `HKCU\...\Run` registry執行 `edge-ai-server.exe --tray` |
**安裝步驟11 步驟,實際實作):**
| # | 步驟名稱 | 進度% | macOS 實作 | Windows 實作 |
|---|---------|-------|-----------|-------------|
| 1 | Extracting server | 5% | 從 `payload/` embed.FS 解壓 binary 到 `~/.edge-ai-platform/` | 解壓到 `%LOCALAPPDATA%\EdgeAIPlatform\` |
| 2 | Creating symlink | 10% | `os.Symlink()``/usr/local/bin/edge-ai-server` | 加入 User PATH (PowerShell `SetEnvironmentVariable`) |
| 3 | Extracting models | 15% | 複製 `data/models.json` + `data/nef/kl520/*.nef` + `data/nef/kl720/*.nef` | 同左 |
| 4 | Extracting scripts | 22% | 複製 `scripts/kneron_bridge.py` + `requirements.txt` + firmware bins | 同左 + 複製 `libusb-1.0.dll` + KneronPLUS .whl |
| 5 | Setting up libusb | 30% | `brew install libusb`(自動安裝 Homebrew 若未安裝) | 複製 `libusb-1.0.dll` 到安裝目錄 |
| 6 | Setting up USB driver | 35% | No-op (macOS 不需要) | **自簽憑證 + pnputil 安裝 WinUSB driver**(見下方詳細流程) |
| 7 | Removing quarantine | 38% | `xattr -cr` 移除 Gatekeeper 隔離標記 | No-op |
| 8 | Setting up Python | 42% | `python3 -m venv` + `pip install -r requirements.txt` | 同左(自動偵測 Python 路徑) |
| 9 | Installing media tools | 78% | `brew install ffmpeg yt-dlp` | `winget install Gyan.FFmpeg` + `winget install yt-dlp.yt-dlp` |
| 10 | Writing config | 85% | 寫入 `~/.edge-ai-platform/config.json`port、relay、語言 | 寫入 `%LOCALAPPDATA%\EdgeAIPlatform\config.json` |
| 11 | Setting up auto-start | 90% | `launchctl load` + LaunchAgent plist | Registry Run key `HKCU\...\Run\EdgeAIPlatformServer` + 立即啟動 `--gui` |
**Windows WinUSB Driver 自動安裝流程Step 6 詳細):**
```
1. 從 payload 解壓 driver 檔案到 installDir/drivers/
├── kneron_winusb.inf (支援 KL520 + KL720 全系列)
└── amd64/
├── WdfCoInstaller01011.dll
└── winusbcoinstaller2.dll
2. PowerShell New-SelfSignedCertificate -Type CodeSigningCert
→ 建立自簽 code signing 憑證
3. certutil -addstore TrustedPublisher <cert>
certutil -addstore Root <cert>
→ 安裝憑證到受信任存儲區
4. pnputil /add-driver kneron_winusb.inf /install
→ 安裝 WinUSB driver 到所有匹配的 Kneron 裝置
→ 支援 Hardware ID:
- USB\VID_3231&PID_0100 (KL520)
- USB\VID_3231&PID_0200 (KL720 KDP)
- USB\VID_3231&PID_0720 (KL720 KDP2)
```
背景Windows 不像 macOS/Linux 有 inbox USB driver 支援。Kneron 裝置需要 WinUSB kernel-level driver 才能被 libusb 存取。之前用戶必須手動使用 Zadig 工具安裝 — 現在完全自動化。
**macOS 無 Homebrew 情境處理:**
@ -2097,29 +2127,38 @@ if !commandExists("python") && !commandExists("python3") {
**解除安裝流程:**
使用者按下 Uninstall 按鈕 → Wails 原生 `MessageDialog` 確認 → 非同步執行:
```go
func (a *Installer) Uninstall(keepData bool) error {
// 1. 停止正在運行的 edge-ai-server 進程
killProcess("edge-ai-server")
func (inst *Installer) ConfirmUninstall() (bool, error) {
// Wails 原生 QuestionDialog跨平台避免 WebView confirm() 不可靠)
result, _ := wailsRuntime.MessageDialog(inst.ctx, wailsRuntime.MessageDialogOptions{
Type: wailsRuntime.QuestionDialog,
Title: "Uninstall Edge AI Platform",
Message: "This will remove the Edge AI Platform...",
})
return result == "Yes", nil
}
// 2. 移除 symlink / PATH
// macOS: os.Remove("/usr/local/bin/edge-ai-server")
// Windows: 從 User PATH 移除安裝目錄
func (inst *Installer) runUninstall() {
// 1. (10%) 停止 server + 移除 auto-restart 服務
// macOS: launchctl unload + 刪除 plist
// Windows: 刪除 Registry Run key + taskkill
// Linux: systemctl --user disable + 刪除 service
// 3. 移除安裝目錄
if keepData {
// 保留 data/custom-models/(使用者自訂模型)
removeAllExcept(installDir, "data/custom-models")
} else {
os.RemoveAll(installDir)
}
// 2. (20%) 移除 symlink / PATH
// macOS/Linux: os.Remove("/usr/local/bin/edge-ai-server")
// Windows: 從 User PATH 移除安裝目錄
// 4. 移除桌面捷徑 / 開機啟動
removeDesktopShortcut()
removeAutoStart()
// 3. (30-85%) 移除平台檔案(保留系統依賴)
// - server binary (30%)
// - data/ 目錄 (45%)
// - scripts/ 目錄 (55%)
// - venv/ 目錄 (70%)
// - config.json + logs/ (85%)
// 5. macOS: 不需要額外清理brew 套件保留)
// Windows: 不移除 Python可能被其他程式使用
// 4. (100%) 若安裝目錄為空,刪除目錄本身
// 系統依賴Python、libusb、Homebrew、ffmpeg保留不動
}
```

View File

@ -734,6 +734,21 @@ func (inst *Installer) GetExistingInstall() (string, error) {
return "", nil
}
// ConfirmUninstall shows a native dialog asking the user to confirm uninstall.
func (inst *Installer) ConfirmUninstall() (bool, error) {
result, err := wailsRuntime.MessageDialog(inst.ctx, wailsRuntime.MessageDialogOptions{
Type: wailsRuntime.QuestionDialog,
Title: "Uninstall Edge AI Platform",
Message: "This will remove the Edge AI Platform and all installed files. System dependencies (Python, libusb) will be preserved.\n\nContinue?",
DefaultButton: "No",
Buttons: []string{"Yes", "No"},
})
if err != nil {
return false, err
}
return result == "Yes", nil
}
// Uninstall removes all installed files.
func (inst *Installer) Uninstall() error {
go inst.runUninstall()

View File

@ -408,8 +408,12 @@ document.getElementById('btn-close').addEventListener('click', () => {
// ── Uninstall ─────────────────────────────────────────────
document.getElementById('btn-uninstall').addEventListener('click', async () => {
if (!confirm(t('uninstall.confirm'))) {
return;
try {
const confirmed = await window.go.main.Installer.ConfirmUninstall();
if (!confirmed) return;
} catch (err) {
// Fallback to JS confirm if native dialog fails
if (!confirm(t('uninstall.confirm'))) return;
}
showStep(4);
document.getElementById('progress-title').textContent = t('uninstall.title');