依 autoflow-agent workspace v2 設計把 PRD / 設計 / 架構 / 交付類 共享文件從個人層 .autoflow/(ignored)搬到 docs/autoflow/(進 git), 讓團隊可共享產品與架構文件,個人層只留 progress / review / testing 等 per-branch 筆記。 - 02-prd/ 21 個檔(PRD、features、market-analysis 等) - 03-design/ 18 個檔(design-spec、wireframes、flows 等) - 04-architecture/ 31 個檔(TDD、design-doc、ADR×14、API 規格等) - 07-delivery/ 3 個檔(project-summary、phase-0.6-handover、stage-deployment-setup) 合計 73 檔。原檔已從 .autoflow/ 移除(migration 工具執行 git mv, 但因 .autoflow/ 在 .gitignore 中、git 將此操作視為新增、無 rename history)。
27 KiB
Pairing 流程設計 — visionA Cloud
雲端版最關鍵的新增流程。使用者要能在雲端 Web 取得 Pairing Token、帶到自己電腦上的 local agent、完成配對,之後就能從瀏覽器遠端控制自己的 Kneron 裝置。
配套的文字版 wireframe 見
wireframes/wf-pairing.md。
1. User Story
作為 一個 Kneron 開發者, 我想要 用一組短短的 token 讓雲端和我桌機上的 Kneron 裝置建立連線, 這樣 我就能從任何地方(出差、客戶端、家裡)用瀏覽器操作我的裝置,不需要每次都連 VPN 或 SSH。
成功條件:
- Token 產生到完成連線 < 3 分鐘
- 使用者不需要懂 tunnel / yamux / WebSocket 等技術細節
- 失敗時能明確告訴使用者哪一步錯、怎麼修
2. 流程全景圖
雲端 Web 使用者電腦 雲端後端
─────── ───────── ────────
local agent
(Go binary)
[登入] ──→ [/devices/pair] ──────────────────────────────────→ POST /api/pairing/token
▼ ↓
產生 token 產生 vAc_ + 32 hex token
▼ (綁 user_id, TTL 15 分鐘)
[PairingTokenCard 顯示 token] ↓
▼ 回傳 token
使用者按「複製」 ←─────
▼
切換到「下載 local agent」分頁
▼
根據 OS 顯示對應下載連結 + 指令
▼
使用者下載、安裝、啟動 local agent
▼ local agent 啟動 ────→ GET /tunnel/connect?token=xxx
▼ ↓
第 3 步:等待連線 WebSocket 升級
▼ yamux session 建立
[polling] GET /api/pairing/status?token=xxx ──────────────────→ ↓
▲ 回 200 + 裝置資訊
│ ↓
◀───────────────────────────────────────────────────────────
▼
「✓ 已連線!」+ 顯示裝置資訊
▼
[繼續] 按鈕
▼
跳 /devices,顯示新裝置
2.1 Token 格式與兩階段 Token 模型
雲端有兩種 token,使用者只會接觸到第一種:
| Token | 用途 | 格式 | TTL | 使用者可見 |
|---|---|---|---|---|
| Pairing Token | 一次性配對 token,使用者貼到 local agent 啟動連線 | vAc_ + 32 字元 hex(總長 36) |
15 分鐘 | ✅ 看得到、要複製 |
| Session Token | Local agent 換到後維持 tunnel 連線用 | 後端規格(使用者不關心) | 90 天 | ❌ 完全看不到 |
換取流程(使用者無感):
1. 雲端 Web 產生 Pairing Token(vAc_xxxx…)→ 使用者複製
2. 使用者貼到 local agent → local agent 用這個 token 打 /tunnel/connect
3. 雲端驗證 Pairing Token → 核發 Session Token 給 local agent
4. Pairing Token 立即失效(one-time use)
5. 後續 local agent 所有連線都帶 Session Token,使用者完全感受不到
UI 顯示策略(36 字元太長、15 分鐘很緊):
- API 回傳完整
vAc_+ 32 hex(一整串,共 36 字元,無空格) - UI 顯示切成兩行提升可讀性(
font-mono text-xl tracking-wider):- 第 1 行:
vAc_前綴 + 前 16 字元 hex(共 20 字元) - 第 2 行:後 16 字元 hex
- 行內用
-或純空白切開都行,切法純視覺;select-all選取時選到整個區塊
- 第 1 行:
- 實際複製到剪貼簿的永遠是無空格、無換行的完整字串(
vAc_a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8) - Mobile(< 640px):字級降為
text-lg,仍切兩行;不允許水平捲動 - 螢幕閱讀器:
aria-label給完整字串,但文字節點用分組避免一字一念
3. 三步式 Stepper
使用既有視覺語彙,以 Tabs 或 Stepper UI 呈現。建議使用 Stepper(因為步驟有順序依賴)。
3.1 視覺
┌─────────────────────────────────────────────────────────────────┐
│ ← 返回 │
│ │
│ 配對新裝置 │
│ 讓你的 Kneron 裝置連上雲端,就能從任何地方遠端操作 │
│ │
│ ●──────────────────●──────────────────○ │
│ 1 2 3 │
│ 取得 Token 設定 Local Agent 確認連線 │
│ │
│ [當前步驟內容] │
│ │
│ [上一步] [下一步] │
└─────────────────────────────────────────────────────────────────┘
3.2 Stepper 規格
- 步驟指示器:
flex items-center gap-2,使用 LucideCircle/CheckCircle - 已完成:
bg-primary text-primary-foreground+CheckCircle填色 - 當前:
ring-2 ring-primary bg-background - 未完成:
bg-muted text-muted-foreground - 步驟間連線:
h-0.5 bg-muted(已完成段落bg-primary) - 下方步驟標題:
text-sm font-medium(已完成/當前)、text-muted-foreground(未完成)
3.3 底部按鈕
- 上一步:
variant=outline,第 1 步 disabled - 下一步:
variant=default,第 3 步改為「完成 → 進入裝置列表」
4. Step 1 — 取得 Token
4.1 版型
┌──────────────────────────────────────────────────────────────┐
│ Step 1 · 取得 Pairing Token │
│ │
│ 複製下方 token,在 Step 2 讓 local agent 使用這組 token 連線雲端 │
│ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ 🔗 你的 Pairing Token │ │
│ │ │ │
│ │ ┌──────────────────────────────────────────────────┐ │ │
│ │ │ vAc_a1b2c3d4e5f6a7b8 │ │ │
│ │ │ c9d0e1f2a3b4c5d6e7f8 │ │ │
│ │ └──────────────────────────────────────────────────┘ │ │
│ │ (視覺切兩行,複製永遠是完整 36 字元無空格) │ │
│ │ │ │
│ │ [📋 複製] [🔄 重新產生] │ │
│ │ │ │
│ │ ⏱ 剩餘 14:52 ─────────────────────── │ │
│ │ 進度條(bg-primary,隨時間變 amber → red) │ │
│ │ 📅 產生時間:14:30 │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ ⚠ 這組 token 15 分鐘內有效,請立刻到 Step 2 完成配對 │
│ token 是一次性使用,完成配對後自動失效 │
│ │
└──────────────────────────────────────────────────────────────┘
4.2 主要元件
PairingTokenCard(見components.md10.2)— 含倒數計時器- 下方安全提示:
Callout樣式(bg-amber-50 border-amber-300,或用 Alert 元件)
4.3 初始狀態
- 進入頁面自動呼叫
POST /api/pairing/token產生新 token(vAc_+ 32 hex,TTL 15 分鐘) - 客戶端啟動 1 秒一次的倒數計時(從
expiresAt - now()算起),不 polling 後端 - 若 API 失敗:顯示 error state 「無法產生 token,請重試」+ 重試按鈕
4.4 倒數計時器視覺規格
TTL 從 72 小時降到 15 分鐘,倒數計時器要顯著且突出,讓使用者意識到要馬上行動:
| 剩餘時間 | 視覺 | 行為 |
|---|---|---|
| > 10:00 | text-foreground + 進度條 bg-primary |
正常顯示 |
| 10:00 – 3:00 | text-amber-600 + 進度條 bg-amber-500 |
文字變粗 font-medium |
| 3:00 – 0:30 | text-red-600 + 進度條 bg-red-500 + 卡片 ring-1 ring-red-300 |
計時文字前加 ⚠;建議配 prefers-reduced-motion 尊重,不閃爍 |
| ≤ 0:30 | 同上 + Toast「Token 即將過期,請立刻完成或重新產生」 | 只 Toast 一次 |
| 00:00 | Token 轉灰 text-muted-foreground line-through;複製按鈕 disabled;顯示「此 token 已過期,請重新產生」 |
aria-live="assertive" 宣告 |
格式:mm:ss(例:14:52、02:17、00:08);不要顯示小時,15 分鐘內絕不超過 15:00。
4.5 互動行為
| 操作 | 行為 |
|---|---|
| 點「複製」 | 呼叫 navigator.clipboard.writeText(token) 複製完整 36 字元無空格字串;按鈕暫態變「已複製 ✓」2 秒;toast「Token 已複製到剪貼簿,15 分鐘內有效」 |
| 點「重新產生」 | 開 AlertDialog:「確定要重新產生?舊 token 將立即失效,新 token 有效期 15 分鐘。」→ 確認後 POST API,更新 UI,重啟倒數 |
| Token 過期(0:00) | 自動將 UI 切至過期狀態;主 CTA 從「下一步」改為「重新產生 token」 |
| 進入 Step 2 前 | 檢查使用者是否已複製(用 state)。未複製則禁止下一步,提示「請先複製 token」或允許繼續但 toast warning |
4.6 i18n key
pairing.step1.title → Step 1 · 取得 Pairing Token
pairing.step1.description → 複製下方 token,15 分鐘內到 Step 2 完成配對
pairing.token.title → 你的 Pairing Token
pairing.copy → 複製
pairing.copied → 已複製
pairing.regenerate → 重新產生
pairing.timeRemaining → 剩餘 {time}
pairing.expiresIn15min → 15 分鐘內有效
pairing.generatedAt → 產生時間:{time}
pairing.security.warning → 這組 token 15 分鐘內有效,請立刻到 Step 2 完成配對
pairing.security.oneTime → token 是一次性使用,完成配對後自動失效
pairing.regenerateConfirm.title → 確定要重新產生?
pairing.regenerateConfirm.description → 舊 token 將立即失效,新 token 有效期 15 分鐘
pairing.toast.copied → Token 已複製到剪貼簿,15 分鐘內有效
pairing.toast.generateFailed → 無法產生 token,請重試
pairing.toast.expiringSoon → Token 即將過期,請立刻完成或重新產生
pairing.token.expired.label → 此 token 已過期,請重新產生
5. Step 2 — 設定 Local Agent
5.1 版型
┌──────────────────────────────────────────────────────────────┐
│ Step 2 · 設定 Local Agent │
│ │
│ 在你的電腦下載並啟動 local agent,讓它連上雲端 │
│ │
│ 選擇作業系統: │
│ [ macOS ] [ Windows ] [ Linux ] │
│ ▲ active │
│ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ 1. 下載 local agent │ │
│ │ [📥 下載 visionA-local-agent-macos.dmg] │ │
│ │ │ │
│ │ 2. 安裝並啟動 │ │
│ │ 打開 DMG,拖曳到 Applications,雙擊啟動 │ │
│ │ │ │
│ │ 3. 輸入 Pairing Token │ │
│ │ 在 local agent 的「雲端」設定頁貼上 token │ │
│ │ │ │
│ │ 或使用命令列(CLI 進階使用者): │ │
│ │ ┌──────────────────────────────────────────────────┐ │ │
│ │ │ visiona-agent --pair-token=\ │ │ │
│ │ │ vAc_a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8 │ │ │
│ │ └──────────────────────────────────────────────────┘ │ │
│ │ (複製時為完整單行無反斜線) │ │
│ │ │ │
│ │ 4. 連線成功後,回到這個頁面進入 Step 3 │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ 💡 已經有 local agent?[跳到 Step 3] │
└──────────────────────────────────────────────────────────────┘
5.2 作業系統分頁
使用 Tabs 元件,三個 tab:macOS / Windows / Linux,自動偵測 navigator.userAgent 設預設。
平台差異:
| OS | 下載檔案 | 安裝步驟 | 命令列 |
|---|---|---|---|
| macOS | .dmg |
拖曳到 Applications | visiona-agent --pair-token=vAc_... |
| Windows | .exe installer |
雙擊執行 | visiona-agent.exe --pair-token=vAc_... |
| Linux | .AppImage |
chmod +x 後執行 |
./visiona-agent --pair-token=vAc_... |
5.3 指令區塊
- 使用
bg-muted font-mono text-sm p-3 rounded-md樣式 - 右上角有「複製」小按鈕
- 將使用者 token 自動嵌入指令(
--pair-token=[當前 token])
5.4 互動
| 操作 | 行為 |
|---|---|
| 切換 OS Tab | 內容重新渲染 |
| 點「下載」按鈕 | 直接下載對應 OS 的 installer |
| 點指令「複製」 | 複製整行 visiona-agent --pair-token=vAc_xxxx…(完整 36 字元 token,單行無跳行) |
| 「已經有 local agent?」 | 直接跳 Step 3 |
5.5 i18n key
pairing.step2.title → Step 2 · 設定 Local Agent
pairing.step2.description → 在你的電腦下載並啟動 local agent...
pairing.step2.selectOS → 選擇作業系統
pairing.step2.download → 下載 local agent
pairing.step2.install → 安裝並啟動
pairing.step2.installHint.macos → 打開 DMG,拖曳到 Applications...
pairing.step2.installHint.windows → 雙擊 installer...
pairing.step2.installHint.linux → chmod +x 後執行...
pairing.step2.enterToken → 輸入 Pairing Token
pairing.step2.tokenHint → 在 local agent 的「雲端」設定頁貼上 token
pairing.step2.orCli → 或使用命令列(CLI 進階使用者):
pairing.step2.waitConnect → 連線成功後,回到這個頁面進入 Step 3
pairing.step2.alreadyHaveAgent → 已經有 local agent?[跳到 Step 3]
6. Step 3 — 確認連線
6.1 視覺(等待中)
┌──────────────────────────────────────────────────────────────┐
│ Step 3 · 確認連線 │
│ │
│ ⏳ 等待 local agent 連線... │
│ │
│ 已等待 0:15(最長 3 分鐘) │
│ │
│ [取消] [查看 Troubleshooting] │
│ │
│ 提示: │
│ • 確認 local agent 已啟動 │
│ • 確認 token 正確無誤 │
│ • 確認你的網路可以連到 cloud.visiona.ai │
└──────────────────────────────────────────────────────────────┘
6.2 視覺(成功)
┌──────────────────────────────────────────────────────────────┐
│ Step 3 · 確認連線 │
│ │
│ ✓ 已成功連線! │
│ │
│ 檢測到的裝置: │
│ ┌────────────────────────────────────┐ │
│ │ 🔌 Kneron KL520 │ │
│ │ Firmware 2.3.1 · Host: office-mac│ │
│ └────────────────────────────────────┘ │
│ │
│ [進入裝置列表 →] │
└──────────────────────────────────────────────────────────────┘
6.3 視覺(失敗 / 超時)
┌──────────────────────────────────────────────────────────────┐
│ Step 3 · 確認連線 │
│ │
│ ⚠ 連線超時 │
│ │
│ 超過 3 分鐘沒收到 local agent 連線 │
│ │
│ 可能原因: │
│ • local agent 尚未啟動 │
│ • token 輸入錯誤 │
│ • 防火牆擋住 WebSocket 連線 │
│ │
│ [重新檢查] [回到 Step 2] [查看完整說明] │
└──────────────────────────────────────────────────────────────┘
6.4 行為
- 進入 Step 3 自動開始 polling
/api/pairing/status?token=xxx(每 3 秒) - 計時器顯示已等待時間
- 成功(
status: connected):停止 polling,顯示成功畫面 + 裝置資訊 - 超時(> 3 分鐘):停止 polling,顯示失敗畫面
- 「取消」:返回
/devices,token 保留(使用者可稍後重試) - 「重新檢查」:重啟 polling
6.5 i18n key
pairing.step3.title → Step 3 · 確認連線
pairing.step3.waiting → 等待 local agent 連線...
pairing.step3.elapsed → 已等待 {time}(最長 3 分鐘)
pairing.step3.cancel → 取消
pairing.step3.troubleshooting → 查看 Troubleshooting
pairing.step3.hints.title → 提示:
pairing.step3.hints.running → 確認 local agent 已啟動
pairing.step3.hints.token → 確認 token 正確無誤
pairing.step3.hints.network → 確認你的網路可以連到 cloud.visiona.ai
pairing.step3.success → 已成功連線!
pairing.step3.success.detected → 檢測到的裝置:
pairing.step3.success.continue → 進入裝置列表 →
pairing.step3.failure.timeout → 連線超時
pairing.step3.failure.reason → 超過 3 分鐘沒收到 local agent 連線
pairing.step3.failure.causes → 可能原因:
pairing.step3.failure.retry → 重新檢查
pairing.step3.failure.backToStep2 → 回到 Step 2
pairing.step3.failure.docs → 查看完整說明
7. 邊界情境
7.1 Pairing Token 過期(15 分鐘後)
- 進入 Step 3 polling 時若收到
status: expired→ 顯示「Token 已過期」+ 「回到 Step 1 重新產生」 - Step 1 若使用者停留超過 15 分鐘未下一步,倒數歸零 UI 會自動切到過期狀態(見 4.4)
- 注意:這裡過期的是 Pairing Token(15 分鐘)。Local agent 一旦換到 Session Token(90 天),後續連線不受影響,使用者看不到這層
7.2 同一 token 被多次使用
- Architect 端決策:token 應該是一次性的(綁第一個連上的 local agent)
- 後續若同 token 被其他 agent 嘗試連線 → Step 3 polling 顯示「此 token 已被其他裝置使用」
7.3 網路中斷(polling 失敗)
- 顯示臨時錯誤 + 自動重試(指數退避)
- 3 次失敗後提示「網路不穩,請檢查連線」
7.4 頁面離開 / 重新整理
- 若 token 已產生但未完成連線:重新進入頁面應該偵測到並回到 Step 3(或重回 Step 1 重新產)
- 使用 URL query param
?token=xxx保存進行中的 token
7.5 多裝置一次配對
- Phase 0:一次只配對一個裝置(一個 token 對一台 agent / 一台 host)
- Phase 1:考慮支援一台 host 多張 Kneron 卡(agent 端回報多裝置)
8. 成功後的導航
完成配對後:
- 在
/devices列表看到新裝置(RemoteDeviceBadge= 在線) - Toast:「裝置 Kneron KL520 已成功配對」
- Activity Timeline 新增一筆
device_paired紀錄
9. 給 Architect / Backend 的 API 契約提議
9.1 POST /api/pairing/token
Pairing Token:vAc_ 前綴 + 32 字元 hex,TTL 15 分鐘,一次性使用。
Request: (需 auth,雛形可 skip auth)
Response 200:
{
"token": "vAc_a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8",
"expiresAt": "2026-04-21T14:45:00Z",
"createdAt": "2026-04-21T14:30:00Z",
"ttlSeconds": 900
}
Session Token(使用者看不到,雲端 ↔ local agent 內部):TTL 90 天,Pairing Token 成功換取後發給 local agent。此層 UI 無關,僅記錄備查。
9.2 GET /api/pairing/status?token=xxx
Response 200:
{
"status": "pending" | "connected" | "expired" | "used",
"device": {
"id": "kl520-usb-0001",
"name": "Kneron KL520",
"type": "KL520",
"firmwareVersion": "2.3.1",
"hostName": "office-mac"
} | null,
"pairedAt": "2026-04-21T14:35:00Z" | null
}
9.3 DELETE /api/pairing/token/:token
重新產生時,先刪除舊的。
9.4 POST /api/devices/:id/unpair
解除配對(從 /devices/[id] 的 DeviceSettingsCard 或 /account 觸發)。
10. 無障礙檢查
- Stepper:
role="list"+ 每個 steprole="listitem"+ 當前 steparia-current="step" - Token 顯示:
aria-label="Pairing token, {token}"(但不念出字元) - 複製按鈕:
aria-live="polite"宣告「已複製」 - Step 3 等待:
aria-live="polite"宣告 status 變更 - 所有按鈕可 Tab 聚焦、Enter / Space 觸發
- 錯誤訊息:
role="alert"
11. TODO
| 項目 | 時機 |
|---|---|
| Local agent UI 的「雲端」分頁設計(接收 token 的那端) | Phase 0 同步設計(需跨 local-tool,另開需求) |
| — | |
| 支援 Email 發送 token(方便跨裝置) | Phase 2 |
| QR Code 配對(手機掃描) | Phase 2 |
| 一次性 pairing code(更短 6 位數字,UX 更好) | Phase 2 |