visionA/docs/autoflow/03-design/flows/flow-pairing.md
jim800121chen fb7da5d180 chore(autoflow): migrate .autoflow/ 共享層文件至 docs/autoflow/
依 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)。
2026-05-04 16:55:55 +08:00

27 KiB
Raw Blame History

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 TokenvAc_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 選取時選到整個區塊
  • 實際複製到剪貼簿的永遠是無空格、無換行的完整字串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,使用 Lucide Circle / 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.md 10.2)— 含倒數計時器
  • 下方安全提示:Callout 樣式(bg-amber-50 border-amber-300,或用 Alert 元件)

4.3 初始狀態

  • 進入頁面自動呼叫 POST /api/pairing/token 產生新 tokenvAc_ + 32 hexTTL 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:5202:1700: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 → 複製下方 token15 分鐘內到 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 元件,三個 tabmacOS / 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顯示失敗畫面
  • 「取消」:返回 /devicestoken 保留(使用者可稍後重試)
  • 「重新檢查」:重啟 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 Token15 分鐘。Local agent 一旦換到 Session Token90 天),後續連線不受影響,使用者看不到這層

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. 成功後的導航

完成配對後:

  1. /devices 列表看到新裝置(RemoteDeviceBadge = 在線)
  2. Toast「裝置 Kneron KL520 已成功配對」
  3. Activity Timeline 新增一筆 device_paired 紀錄

9. 給 Architect / Backend 的 API 契約提議

9.1 POST /api/pairing/token

Pairing TokenvAc_ 前綴 + 32 字元 hexTTL 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. 無障礙檢查

  • Stepperrole="list" + 每個 step role="listitem" + 當前 step aria-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另開需求
Token 過期警告 → 已納入 Phase 0見 4.4 倒數計時器,因 TTL 降至 15 分鐘必做)
支援 Email 發送 token方便跨裝置 Phase 2
QR Code 配對(手機掃描) Phase 2
一次性 pairing code更短 6 位數字UX 更好) Phase 2