# Code Reuse Plan — visionA-local > 從 `/Users/jimchen/Innovedus/edge-ai-platform/edge-ai-platform/` 取用程式碼的完整對照表。 > 三種策略:**直接複製**、**改寫**、**新寫**。 --- ## 1. 總體比例(預估) - **直接複製**:~60% LOC - **改寫**:~20% - **新寫**:~20% - **刪除**:獨立計算,見 [`removed-code.md`](./removed-code.md) ## 2. 目錄層級策略 | From(edge-ai-platform) | To(local_tool) | 策略 | 備註 | |------------------------|----------------|------|------| | `server/main.go` | `server/main.go` | **改寫** | 移除 cluster / tunnel / relay / hwid / gitea 邏輯 | | `server/go.mod` | `server/go.mod` | **改寫** | 移除不再需要的 imports;module name 可改為 `visiona-local` | | `server/go.sum` | `server/go.sum` | **改寫** | 跟著 go.mod 重新生成 | | `server/internal/api/router.go` | `server/internal/api/router.go` | **改寫** | 見 [`api-endpoints.md`](./api-endpoints.md) §4 | | `server/internal/api/middleware.go` | 同路徑 | **直接複製** | | | `server/internal/api/handlers/` | 同路徑 | **部分複製 / 改寫** | 見下方 §3.1 | | `server/internal/api/ws/` | 同路徑 | **部分複製 / 改寫** | 見下方 §3.2 | | `server/internal/api/api_e2e_test.go` | 同路徑 | **改寫** | 刪除 cluster / flash / auth test case | | `server/internal/camera/` | 同路徑 | **直接複製** | 除了 ffmpeg 路徑查找改為「優先 bundled」,見 §3.4 | | `server/internal/config/config.go` | 同路徑 | **改寫** | 見 §3.3 | | `server/internal/deps/` | 同路徑 | **改寫** | 移除 update / relay 相關檢查 | | `server/internal/device/` | 同路徑 | **直接複製** | 核心裝置管理 | | `server/internal/driver/` | 同路徑 | **直接複製** | | | `server/internal/inference/` | 同路徑 | **直接複製** | | | `server/internal/model/` | 同路徑 | **直接複製** | | | `server/internal/flash/` | ❌ **刪除** | 使用者決策 Q9 | | `server/internal/cluster/` | ❌ **刪除** | 使用者決策 | | `server/internal/tunnel/` | ❌ **刪除** | 使用者決策 | | `server/internal/update/` | ❌ **刪除** | 使用者決策 Q6 | | `server/pkg/logger/` | 同路徑 | **直接複製** | | | `server/pkg/hwid/` | ❌ **刪除** | 只給 relay 用的 | | `server/cmd/relay-server/` | ❌ **刪除** | 不需要 relay | | `server/tray/` | ❌ **刪除** | 使用者決策 Q-A=A3:砍掉 tray,省跨平台圖資產與 Wails tray 踩坑 | | `server/scripts/kneron_bridge.py` | `server/scripts/kneron_bridge.py` | **直接複製** | | | `server/scripts/requirements.txt` | 同路徑 | **直接複製** | | | `server/scripts/update_kl720_firmware.py` | ❌ **刪除** | flash 已砍 | | `server/scripts/firmware/` | ❌ **刪除** | 同上 | | `server/data/models.json` | 同路徑 | **直接複製** | | | `server/data/nef/kl520/` | 同路徑 | **直接複製** | 全部預置模型 | | `server/data/nef/kl720/` | 同路徑 | **直接複製** | | | `server/web/` | 同路徑(空目錄) | **直接複製** | go:embed 掛載點 | | `frontend/` | `frontend/` | **改寫** | M1 就要清乾淨:刪除 cluster / relay / tunnel 相關頁面、元件、store、API client,讓 `pnpm build` 通過且 UI 乾淨(使用者決策 Q-C=C2) | | `installer/` | `visiona-local/` | **改寫(改名 + 精簡)** | 見 §3.5 | | `installer/wheels/` | `vendor/wheels/` 或 `visiona-local/payload/scripts/wheels/` | **直接複製** | KneronPLUS wheel | | `installer/drivers/` | `vendor/drivers/` 或 `visiona-local/payload/drivers/` | **直接複製** | Windows WinUSB | | `installer/payload/` | `visiona-local/payload/` | **結構沿用,內容重建** | 由 Makefile payload target 重新 stage | | `Makefile` | `Makefile` | **改寫** | 見 [`build-pipeline.md`](./build-pipeline.md) | | `docker/` | ❌ **刪除** | | | `scripts/deploy-*.sh` | ❌ **刪除** | | | `scripts/kneron_detect.py` | `server/scripts/kneron_detect.py` | **直接複製** | installer 裝置偵測用 | | `tools/` | 視檔案決定 | **多數刪除** | 只保留開發相關的 script | | `docs/` | ❌ **刪除**(之後重寫) | | | ## 3. 需要改寫的檔案細節 ### 3.1 `server/internal/api/handlers/` | 檔案 | 策略 | 動作 | |------|-----|-----| | `system_handler.go` | 改寫 | 移除 `CheckUpdate`、`giteaURL` 參數;刪除 `update-check` handler | | `model_handler.go` | 直接複製 | | | `model_upload_handler.go` | 直接複製 | | | `device_handler.go` | 改寫 | 移除 `FlashDevice` handler 與 `flashSvc` 注入 | | `camera_handler.go` | 直接複製 | 但 `StartFromURL` 內部的 yt-dlp 路徑查找改為 bundled(見 §3.4) | | `cluster_handler.go` | ❌ 刪除 | | ### 3.2 `server/internal/api/ws/` | 檔案 | 策略 | |------|-----| | `hub.go` | 直接複製 | | `device_events.go` | 直接複製 | | `server_logs.go` | 直接複製 | | `inference.go` | 直接複製 | | `flash_progress.go` | ❌ 刪除 | | `cluster_*.go` | ❌ 刪除 | ### 3.3 `server/internal/config/config.go` 改寫版 ```go package config import ( "flag" "fmt" "os" "path/filepath" ) type Config struct { Port int Host string MockMode bool MockCamera bool MockDeviceCount int LogLevel string DevMode bool PythonBin string // 新增:由 Wails app 傳入 ScriptsDir string // 新增 DataDir string // 新增 // ❌ 以下全部移除: // RelayURL, RelayToken, GUIMode, GiteaURL } func Load() *Config { cfg := &Config{} flag.IntVar(&cfg.Port, "port", 3721, "Server port") flag.StringVar(&cfg.Host, "host", "127.0.0.1", "Server host (always localhost)") flag.BoolVar(&cfg.MockMode, "mock", false, "Enable mock device driver") flag.BoolVar(&cfg.MockCamera, "mock-camera", false, "Enable mock camera") flag.IntVar(&cfg.MockDeviceCount, "mock-devices", 1, "Number of mock devices") flag.StringVar(&cfg.LogLevel, "log-level", "info", "Log level") flag.BoolVar(&cfg.DevMode, "dev", false, "Dev mode") flag.StringVar(&cfg.PythonBin, "python", "", "Path to python3 interpreter") flag.StringVar(&cfg.ScriptsDir, "scripts-dir", "", "Path to scripts directory") flag.StringVar(&cfg.DataDir, "data-dir", "", "Path to data directory") flag.Parse() // 合理 default(dev 模式) if cfg.ScriptsDir == "" { cfg.ScriptsDir = filepath.Join(".", "scripts") } if cfg.DataDir == "" { cfg.DataDir = filepath.Join(".", "data") } return cfg } func (c *Config) Addr() string { return fmt.Sprintf("%s:%d", c.Host, c.Port) } ``` ### 3.4 ffmpeg / yt-dlp 路徑查找 新增 `server/internal/camera/binaries.go`: ```go package camera import ( "os" "path/filepath" "runtime" ) // FfmpegBinary returns the ffmpeg binary to use, preferring bundled over PATH. func FfmpegBinary() string { if p := os.Getenv("VISIONA_FFMPEG"); p != "" { return p } if p := bundled("ffmpeg"); p != "" { return p } return "ffmpeg" // PATH fallback } func YtDlpBinary() string { if p := os.Getenv("VISIONA_YTDLP"); p != "" { return p } if p := bundled("yt-dlp"); p != "" { return p } return "yt-dlp" } func bundled(name string) string { exe, err := os.Executable() if err != nil { return "" } dir := filepath.Dir(exe) candidate := filepath.Join(dir, name) if runtime.GOOS == "windows" { candidate += ".exe" } if _, err := os.Stat(candidate); err == nil { return candidate } return "" } ``` ### 3.5 `installer/app.go` → `visiona-local/app.go` 改寫 原 894 行,改寫要點: **移除的欄位 / 函式**: - `relayURL`, `relayToken`, `dashboardURL` - `GetDashboardURL`, `GenerateToken`, `OpenBrowser` 中任何跟 relay 有關的邏輯 - `stepInstallUSBDriver` 需要保留,但只給 Windows 用(flash driver 已砍但 WinUSB 還需要) - `stepInstallFfmpeg` **改寫**:不去系統 PATH 找,而是從 payload/bin 解壓 bundled ffmpeg **新增的欄位 / 函式**: - `pythonMode`(`bundled` / `system` / `auto`) - `stepSetupPythonStandalone`(解壓內嵌 python-build-standalone) - `stepSetupPythonSystem`(fallback 到系統 python,沿用舊的 `setupPythonVenv` 邏輯) - `stepInstallWheelsOffline`(用 `pip install --no-index --find-links`) - `i18n` translator 注入 - `launchServer` / `stopServer` / `watchServer`(見 [`tray-and-lifecycle.md`](./tray-and-lifecycle.md) §4) - `single-instance lock` **保留(幾乎不動)**: - `GetSystemInfo`, `BrowseDirectory`, `ValidatePath` - `stepCreateDir`, `stepExtractBinary`, `stepExtractData`, `stepExtractScripts` - `DetectHardware`, `parseDetectOutput` - `extractFile`, `extractDir` - Platform-specific 檔案(`platform_darwin.go` / `platform_linux.go` / `platform_windows.go`) ## 4. 新寫的程式碼 完全新寫的檔案: ### 4.1 Wails app 側 ``` visiona-local/ ├── python_runtime.go ← python-build-standalone 解壓 + 版本偵測 ├── server_launcher.go ← spawn / stop / watch Go server ├── lifecycle.go ← single-instance lock、port picking、cleanup ├── ipc.go ← /ipc/raise 小型 HTTP listener ├── i18n.go ← Translator + locales embed └── locales/ ├── en.json └── zh-TW.json ``` ### 4.2 Build / CI ``` scripts/ ├── vendor-sync.sh ← 下載 python-build-standalone、ffmpeg、yt-dlp 到 vendor/ ├── build-appimage.sh ← Linux AppImage 打包腳本 ├── check-i18n.sh ← 檢查 i18n key 一致性 └── smoke-test.sh ← 安裝後 smoke test visiona-local-installer.iss ← Inno Setup 腳本 dmg-config.py ← macOS dmgbuild 設定 .github/workflows/release.yml ← CI(選配) ``` ## 5. 搬家步驟(建議執行順序) 1. **建立骨架**:`mkdir -p local_tool/{server,frontend,visiona-local,vendor,scripts,dist}` 2. **複製 server core**:`cp -r edge-ai-platform/server/{internal/{api,camera,config,deps,device,driver,inference,model},pkg/logger,scripts,data,web} local_tool/server/` 3. **跳過要刪的 package**:不要複製 `cluster`、`tunnel`、`flash`、`update`、`pkg/hwid`、`cmd/relay-server`、`tray`(使用者決策 Q-A:砍 tray) 4. **改寫 `main.go` + `config.go` + `router.go`**(見上方 §3) 5. **改寫 go.mod**:`go mod init visiona-local/server` → `go mod tidy`(會自動清掉 unused imports) 6. **驗證 `go build ./...` 通過** 7. **複製 frontend**:`cp -r edge-ai-platform/frontend local_tool/frontend` 8. **清理前端 cluster / relay / tunnel UI**(使用者決策 Q-C=C2:M1 就要清乾淨,不留到 M2):刪除 `src/app/clusters/`、`src/app/workspace/cluster/`、`src/components/cluster/`、`src/components/relay-token-sync.tsx`、`src/lib/api/clusters.ts`、`src/lib/api/tunnel.ts`(若有)、`src/lib/api/update.ts`(若有),修改 `sidebar.tsx` 移除 Clusters 導航項、`page.tsx` 移除 cluster stat、`settings/page.tsx` 移除 relay / cluster 區塊。最後驗證 `pnpm build` 通過且 UI 乾淨。 9. **複製 installer**:`cp -r edge-ai-platform/installer local_tool/visiona-local`,改 `main.go` 的 app 名稱與 bundle ID 10. **先跑 M1-12**:全新機器上 installer 能裝起來並跑通 Mock 模式