jim800121chen c54f16fca0 Initial commit: visionA monorepo with local-tool subproject
local-tool/: visionA-local desktop app
- M1: Wails shell + Go server + Next.js UI + Mock mode (macOS dmg ready)
- M2: i18n (zh-TW/en) + Settings 4-tab refactor
- M3: Embedded Python 3.12 runtime (python-build-standalone) + KneronPLUS wheels
- M4: Windows Inno Setup script (build on Windows runner)
- M5: Linux AppImage script + udev rule (build on Linux runner)
- M6: ffmpeg (GPL, pending legal review) + yt-dlp bundled
- Lifecycle: watchServer health check, fatal native dialog,
            Wails IPC raise endpoint, stale process cleanup

.autoflow/: full PRD / Design Spec / Architecture / Testing docs
            (4 rounds tri-party discussion + cross review)
.github/workflows/: macOS / Windows / Linux build CI

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 22:10:38 +08:00

217 lines
11 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# M1-3 Review — 改寫 main/config/router + 清 import + module rename
- 審查者Reviewer Agent
- 日期2026-04-10
- 審查對象:`/Users/jimchen/visionA/local-tool/server/`
- 依據文件:`api-endpoints.md §4``removed-code.md §3~§6``M1-2-review.md`
## 結論:✅ 通過
M1-3 的所有目標達成module 已改名為 `visiona-local/server`、四個要刪的檔案全數刪除、CLI flag 對齊規格、router/config/main/handlers/test 全部清乾淨,`go build ./...``go vet ./...` 一次通過。可以直接進入 M1-5。
---
## 檢查清單
### A. 要刪除的檔案removed-code.md §2
| 檔案 | 狀態 |
|------|------|
| `internal/api/handlers/cluster_handler.go` | ✅ 已刪除 |
| `internal/api/ws/flash_ws.go` | ✅ 已刪除 |
| `internal/api/ws/cluster_flash_ws.go` | ✅ 已刪除 |
| `internal/api/ws/cluster_inference_ws.go` | ✅ 已刪除 |
剩餘 `internal/api/ws/` 下只剩:`device_events_ws.go``hub.go``inference_ws.go``server_logs_ws.go`,符合 api-endpoints.md 中保留的 WebSocket 清單。
剩餘 `internal/api/handlers/` 下只剩:`camera_handler.go``device_handler.go``model_handler.go``model_upload_handler.go``system_handler.go`,乾淨。
### B. Module 改名
| 檢查項 | 結果 |
|-------|------|
| `go.mod` module 行 | ✅ `module visiona-local/server` |
| `grep -rn "edge-ai-platform" server/ --include="*.go"` | ⚠️ 僅命中 `driver/kneron/detector.go` L32-33 兩處**字串 literal**`~/.edge-ai-platform/venv/...`**非 import path** |
| 全檔 `edge-ai-platform/` import | ✅ 0 處 |
`detector.go` 內的路徑字串是掃描使用者家目錄找既有 Python venv 的 fallback 路徑,不是 module import**符合 M1-3 要求**(僅 import 需更新)。列為 🟢 備註:等 installer / python runtime 重設計時再調整這兩個 legacy 路徑。
### C. CLI flag 對齊config.go
| Flag | 規格 | 實際 | 結果 |
|------|------|------|------|
| `--mock` | 必須 | ✅ bool | ✅ |
| `--dev` | 必須 | ✅ bool | ✅ |
| `--port` | 必須 | ✅ intdefault 3721 | ✅ |
| `--data-dir` | 必須 | ✅ string | ✅ |
| `--python-mode` | 必須 | ✅ stringauto/bundled/system | ✅ |
| `--mock-camera` | 保留 | ✅ bool | ✅ 合理 |
| `--mock-devices` | 保留 | ✅ int | ✅ 合理 |
| `--log-level` | 保留 | ✅ string | ✅ 合理 |
| `--model-dir` | 保留 | ✅ string | ✅ 合理 |
| `--host` | 保留(強制覆寫) | ✅ stringdefault 127.0.0.1 | ✅ 見 D |
| `--relay-url` | **必砍** | ❌ 不存在 | ✅ |
| `--relay-token` | **必砍** | ❌ 不存在 | ✅ |
| `--tray` | **必砍** | ❌ 不存在 | ✅ |
| `--gui` | **必砍** | ❌ 不存在 | ✅ |
`PythonMode` 定義為 typed string + 三個常數 + 預設 fallback乾淨且符合 api-endpoints.md §5.2 未來要擴充的規劃。
### D. Host 強制 127.0.0.1
`config.go` L48-49
```go
// 強制 localhost-only
cfg.Host = "127.0.0.1"
```
**確實 override**。在 `flag.Parse()` 之後無條件覆寫,即使使用者傳 `--host 0.0.0.0` 也會被改回 `127.0.0.1`。防呆正確。
### E. Router 乾淨度api-endpoints.md §4
| 檢查項 | 結果 |
|-------|------|
| 簽章移除 `clusterMgr``flashSvc``relayToken` 三參數 | ✅ |
| `/api/clusters/*` routes | ✅ 0 處 |
| `/api/devices/:id/flash` | ✅ 已移除 |
| `/api/system/update-check` | ✅ 已移除 |
| `/auth/token` + OPTIONS | ✅ 已移除 |
| `/ws/devices/:id/flash-progress` | ✅ 已移除 |
| `/ws/clusters/*` | ✅ 0 處 |
| 保留的 REST routes 與 §4 清單逐項比對 | ✅ 完全符合 |
| 保留的 WS routesdevices/events, devices/:id/inference, server-logs | ✅ 三條都在 |
| grep `clusterMgr|flashSvc|/api/clusters|/auth/token|update-check|/flash-progress|CheckUpdate` | ✅ 0 matches |
### F. main.go 清理removed-code.md §3, §4
| 檢查項 | 結果 |
|-------|------|
| imports 內無 `cluster/flash/tunnel/hwid` | ✅ |
| 沒有 `tunnelClient``clusterMgr``flashSvc``relayToken` 變數 | ✅ |
| 沒有 `relayWebURL``openBrowser` 函式 | ✅ |
| `NewSystemHandler` 呼叫移除 `giteaURL` | ✅ L147 只傳 `Version, BuildTime, restartFn` |
| `NewRouter` 呼叫移除 3 個參數 | ✅ L150 簽章對齊 |
| 保留 graceful shutdown + self-restart exec | ✅ |
| 保留 `killExistingProcess` | ✅ 合理(解決 port 被占用) |
### G. device_handler.go
| 檢查項 | 結果 |
|-------|------|
| 移除 `internal/flash` import | ✅ |
| 移除 `FlashDevice` handler | ✅(檔內已無) |
| 建構子 `NewDeviceHandler` 不再注入 `flashSvc` | ✅ 只收 deviceMgr/inferenceSvc/wsHub |
| 保留 Connect/Disconnect/Scan/List/Get/Start/Stop Inference | ✅ |
### H. system_handler.go
| 檢查項 | 結果 |
|-------|------|
| 移除 `internal/update` import | ✅ |
| 移除 `CheckUpdate` handler 與 `giteaURL` 欄位 | ✅ |
| 保留 Health/Info/Metrics/Deps/Restart | ✅ |
**備註**api-endpoints.md §1.1 有要求 `/api/system/info` 擴充 `actual_port``mode``python_mode` 三個欄位,以及新增 `/api/system/mode``/api/system/python-runtime``/api/system/port` 三個 endpoint。目前 `Info()` 只回傳 `version/platform/uptime/goVersion`,新 endpoint 也尚未新增。**但這些是後續 milestoneM2 以後)的工作,不屬於 M1-3 的範圍**M1-3 的任務是 *清理*,不是新增功能)。列入待辦追蹤,不影響本次通過。
### I. api_e2e_test.go
| 檢查項 | 結果 |
|-------|------|
| 移除 `cluster` / `flash` import | ✅ |
| 移除 cluster / flash / auth 測試案例 | ✅ 檔內無殘留 |
| `setupTestServer` 呼叫 `NewRouter` 簽章對齊 | ✅ L50-53 |
| 保留的測試Health、DeviceWorkflow、DeviceScan、ModelList、ConnectNonExistent、MultiDeviceIsolation | ✅ |
---
## Build 驗證
自己實際跑的結果:
```bash
cd /Users/jimchen/visionA/local-tool/server
go build ./... → exit 0無任何輸出
go vet ./... → exit 0無任何輸出
```
**完全乾淨**,無 compile error、無 vet warning。
---
## 針對審查重點 6、7、8 的判斷
### 6. CORS 中的 `X-Relay-Token` headermiddleware.go L19
```go
c.Header("Access-Control-Allow-Headers", "Content-Type, Authorization, X-Relay-Token")
```
**判斷:🟢 Minor無害但可清**
- `relay-url/relay-token` flag、`RelayToken` config、`/auth/token` endpoint 都已移除,前端也不會再送這個 header。保留在 CORS allow-list 屬於**無害殘留**(瀏覽器只會放行,不會主動產生請求)。
- Backend 的判斷「保留但可清」合理。建議 M1-3 收尾或 M1-5 之前順手改成 `"Content-Type, Authorization"`,避免將來審計時引起疑問,但不阻擋進入 M1-5。
### 7. `internal/driver/` 裡的 `Flash` / `FlashProgress`
讀了 `internal/driver/interface.go``driver/kneron/kl720_driver.go:397` 附近、`driver/mock/mock_driver.go:68`,結論:
- `DeviceDriver` 介面的 `Flash(modelPath, progressCh)` 是**把 .nef 模型載入到 Kneron 裝置**的方法,對應 Kneron SDK 的 model load API`kl720_driver.go` 的註解明確寫「models can be freely reloaded」並搭配 `scripts/kneron_bridge.py`)。
- 這跟已刪除的 `internal/flash` package韌體燒錄服務完全是兩回事 —— 後者是 firmware flashingKL720 firmware binary → device flash memory前者是 model loading把已訓練好的 .nef 模型丟到裝置的 RAM/ flash 區)。
- 既然 `/api/devices/:id/inference/start` 仍然需要先把模型載上去,這個 `Flash` 方法是**必要的**,不能刪。
**Backend 的判斷正確,列為 ✅ 無問題**。唯一 cosmetic 的點:未來可考慮把 `Flash` / `FlashProgress` 改名為 `LoadModel` / `LoadProgress` 避免語意混淆,但屬於重構,不在 M1-3 範圍。
### 8. `web/out/placeholder.txt` 解法
確認:
- `web/embed.go``//go:embed all:out` 指向 `out/` 目錄
- 因為 frontend 尚未 build原本 `out/` 不存在會導致 `go build` 失敗M1-2 review 已預警)
- M1-3 在 `out/` 下放了一個 `placeholder.txt`,讓 embed target 存在build 即可通過
**判斷:🟢 合理的最小修補**`all:` 前綴會一起 embed 底線或點開頭的檔案,但 placeholder.txt 不影響實際 static serving因為等 frontend build 完就會覆蓋整個 `out/`)。簡單、正確、不擾動 embed.go。
---
## 問題
### 🔴 Critical
無。
### 🟡 Major
無。
### 🟢 Minor / Suggestion
1. **`middleware.go` L19 的 `X-Relay-Token`** — 無害殘留,建議之後收尾時清掉:
```go
c.Header("Access-Control-Allow-Headers", "Content-Type, Authorization")
```
2. **`driver/kneron/detector.go` L32-33 的 `.edge-ai-platform` 字串** — legacy 路徑字串,不是 import path不影響 M1-3但未來 installer/python runtime 重設計時應跟著改名(例如改為 `.visiona-local/venv/...`)。
3. **`/api/system/info` 尚未擴充 `actual_port` / `mode` / `python_mode`,以及 `/api/system/mode`、`/api/system/python-runtime`、`/api/system/port` 三個新 endpoint 尚未實作** — 屬於 M2 以後的工作api-endpoints.md §1.1, §5**不阻擋 M1-3 / M1-5**,但要記得追蹤。
4. **`Flash` / `FlashProgress` 命名語意模糊** — 未來重構時可考慮改為 `LoadModel` / `LoadProgress`,非必要。
### 優點
- Module rename 徹底、Go import 0 殘留,找不到任何 `edge-ai-platform/` import path。
- Router 與 api-endpoints.md §4 的規格**逐行對齊**,連註解順序都維持原作者風格。
- `config.go` 的 Host 強制 override 防呆寫得乾淨flag 還是允許傳入便於開發者習慣,但結尾強制覆寫,二者兼顧)。
- `PythonMode` 用 typed string + fallback 預設值,為 M2 的 runtime 切換邏輯鋪好路。
- `api_e2e_test.go` 精簡得宜,保留了關鍵 workflow 測試build + 編譯都乾淨(雖然本次沒跑 `go test`,但 vet 通過代表 test file 的 symbol 引用都對)。
- `killExistingProcess` 保留是明智的 — 對本地 daemon 很實用,開發時切換 binary 不會被 stale process 擋住。
---
## 可以進入 M1-5build Go binary
✅ **可以直接進入 M1-5**。
理由:
1. `go build ./...` 與 `go vet ./...` 在 reviewer 端重現,皆一次通過。
2. 所有刪除清單、module rename、router 簽章、CLI flag、Host 強制覆寫都對齊規格。
3. `web/out/placeholder.txt` 已解決 M1-2 預警的 embed 編譯障礙,`go build` 不再卡在 embed target 缺失。
4. 剩餘的 🟢 項目CORS header、detector 路徑字串、system info 擴充)都不影響 binary 能不能 build也不影響 binary 跑起來的核心功能,可延後處理。
建議 M1-5 時:
- 跑一次 `go test ./...` 當作額外保險(本次 review 未跑,僅跑 build + vet
- 驗證 `go build -o visiona-local-server .` 產出 binary 並直接執行一次,確認 `--mock --dev --port 3721` 能成功起 server。
- 順手把 🟢#1CORS header清掉避免審計疑問。