feat(robot): connect provisioning hotspots in app

This commit is contained in:
2026-08-16 20:18:28 +08:00
parent abecd5f344
commit c1326a2980
17 changed files with 1950 additions and 11 deletions

View File

@@ -0,0 +1,87 @@
# Proposal: Robot 配网页内连接 Windows 与 macOS 热点
## Status
Proposed for promotion. Implementation and automated verification are complete; signed macOS and physical-Robot platform verification remain open acceptance gates.
## Date
2026-08-16
## Supersedes
- 仅替换 ADR-002 中 `connect_device_ap` 依赖用户前往操作系统 Wi-Fi 面板手动选网的步骤。
- 不改变 ADR-002 的固件零改动、固定 Portal、Wi-Fi 凭据隔离、六位码 Binding、`bound` 语义和精确 `=0` 回滚。
## Context
当前引导在用户确认机器人进入配网模式后,只说明如何前往系统 Wi-Fi 设置连接 `Xiaozhi-*` 热点。用户希望在 Makelore 弹窗内直接看到附近可连接的机器人热点并完成连接,而且 Windows 与 macOS 必须同时交付。
已审计固件源码使用 `Xiaozhi` 前缀生成配网热点名称,但确切出货镜像仍是发布门禁。现有固件热点为开放网络;发现热点不能证明设备身份、所有权、账号 Binding 或在线状态。
## Decision
Makelore 在现有 `connect_device_ap` 步骤中加入一个 Main-owned Robot Hotspot Module,并通过两个平台 Adapter 实现 Windows 与 macOS 原生扫描、连接和当前 SSID 核验。
Module 对 Host route 暴露两个小操作:
- `scan()` 返回本次扫描内有效的 Robot 热点候选。
- `connect(candidateId)` 只接受本次扫描生成的短期不透明候选 ID,连接并核验当前 SSID 后返回成功。
Renderer 只通过 `src/lib/ai-hardware.ts` 的 typed Host seam 使用该 Module。Renderer 不接触原生网络接口、BSSID、接口名、系统 profile、命令行、任意 SSID 或任意 URL。
Host API surface:
- `POST /api/works/ai-hardware/provisioning-hotspots/scan`:无 query,exact body `{}`;成功 envelope 的 `data` 为 `{ platform, hotspots }`,其中热点只有 `{ candidate_id, ssid, signal_percent, connected }`。
- `POST /api/works/ai-hardware/provisioning-hotspots/connect`:无 query,exact body `{ candidate_id }`;成功 envelope 的 `data` 为 `{ connected: true, candidate_id, ssid }`。
- 两个操作都必须先检查 Main-owned `guidedHotspotBinding` capability,并在获取 Works token 或访问 upstream 前本地返回。
- 逻辑失败沿用 outer HTTP 200 的标准 Host error envelope;稳定错误码覆盖 unsupported、permission denied、busy、scan failed、candidate expired 和 connect failed。原生错误文本不得投影到 Renderer。
平台实现:
- Windows Adapter 直接使用系统 WLAN API 扫描开放网络、以临时 discovery connection 连接,并轮询当前 connection 验证 SSID;不得解析本地化的 `netsh` 输出,也不得保存永久 profile。
- macOS Adapter 直接使用 CoreWLAN 扫描和关联,并由 Main 侧 CoreLocation 权限流程满足现代 macOS 的 SSID 访问要求;应用包声明用途文本。不得依赖已弃用或系统版本不稳定的 `airport` CLI。
- 两个平台 Adapter 隔离在 Module 后;不支持的平台安全返回 unsupported。
## Candidate And Operation Rules
- Main 只保留 SSID 以 `Xiaozhi-` 开头、名称可打印且长度不超过 32 bytes、明确为开放网络的候选。
- 多个同名 BSSID 在 Renderer 合并为一个 SSID 候选,使用最强信号项;BSSID 永不跨越 Main 边界。
- 候选 ID 使用不可预测随机值,下一次扫描、显式 clear、进程重启或短期过期后失效。
- 同一 Module 同时只运行一个 native operation;扫描和连接都有硬超时。
- 不自动连接最强热点。用户必须明确选择并触发连接。
- `connect` 只有在当前 SSID 与候选 SSID 完全一致时才成功;原生关联请求已返回但未验证时不得推进流程。
## UX And Recovery
进入 `connect_device_ap` 时自动扫描,页面展示候选、信号强度、重新扫描和明确连接操作。连接中锁定会话状态;成功核验后进入固定 Portal 步骤。
权限拒绝、没有候选、超时或平台调用失败时,页面提供安全、可行动的说明,并保留“我已在系统中连接”的人工兜底。取消/关闭/重新打开向导会使旧的异步结果失效,不能污染新的向导会话。
产品文案继续提示开放热点与临时断网风险;Makelore 不收集热点密码,也不收集、代理或保存家庭 Wi-Fi 密码。家庭 Wi-Fi 凭据仍只在固件 Portal 内输入。
## Security And Privacy
- 系统级网络访问只存在于 Electron Main;Host bearer authentication 仍先于 route dispatch。
- 仅允许连接最近一次扫描获得的开放 `Xiaozhi-*` 候选,客户端不能提交任意 SSID。
- 日志不得包含 BSSID、系统接口、profile、家庭 Wi-Fi 凭据、原生错误或位置数据。
- 热点发现是未经认证的便利功能。恶意热点可冒充前缀;默认开启不等于安全身份发现。
## Acceptance Gates
在声称双平台硬件流程完成前,必须同时满足:
1. Windows 聚焦测试、打包加载检查和真实系统扫描/连接 Robot 热点 smoke。
2. macOS x64/arm64 打包加载、位置权限、扫描、连接和当前 SSID 核验 smoke。
3. Renderer -> Host API -> Main Module 的 Electron E2E,覆盖成功与权限/无热点/连接失败兜底。
4. 确切出货固件热点命名、开放网络属性、Portal 地址和六位码发行契约核验。
5. exact `NIANCODE_AI_HARDWARE_GUIDED_HOTSPOT_BINDING=0` 仍关闭整个引导路径,直接六位码 Binding 不回归。
Windows 开发机上的自动化测试不能替代 macOS 实机证明;在 macOS smoke 完成前,代码可以达到实现完成,但发布证据必须明确标记为未完成。
## Consequences
- 用户无需先离开 Makelore 即可选择并连接机器人热点,失败时仍能回到系统设置完成操作。
- 固件、云端 Binding 和家庭 Wi-Fi 密码边界保持不变。
- 应用新增跨平台 native dependency 与 macOS 位置用途声明,安装包和签名验证范围扩大。
- 未来若需要可信设备身份或自动 claim,仍必须另立经过认证的 BLE/制造身份/云契约方案;本决策不能被扩展解释为已解决该问题。

View File

@@ -0,0 +1,112 @@
# Task: Implement in-app Robot hotspot scan and connection on Windows and macOS
## Identity
- Task ID: 20260816-robot-hotspot-connect-5e7a91
- Mode: Feature
- Branch: codex/20260816-robot-hotspot-connect-5e7a91-robot-hotspot-connect
- Worktree: D:\Datas\OthersProjects\makelore-robot-hotspot-connect-5e7a91
- Base commit: abecd5f34485ab467e5f032c618083d88e34b74d
- Owner: codex-root
- Status: Complete
## Scope
- Replace the manual `connect_device_ap` step with in-dialog discovery, explicit selection, connection, and verification of nearby open `Xiaozhi-*` Robot hotspots on both Windows and macOS.
- Add one Main-owned Robot hotspot Module with Windows and macOS Adapters, a strict typed Host interface, safe Renderer projection, permission/error fallbacks, and focused tests.
- Keep the current firmware, fixed portal, Wi-Fi credential entry, Activation, six-digit Binding, and exact `NIANCODE_AI_HARDWARE_GUIDED_HOTSPOT_BINDING=0` rollback unchanged.
- Add task-scoped decision/evidence records and promotion candidates; do not write accepted ADRs or canonical shared memory from feature mode.
## Intent And Constraints
- The user explicitly confirmed simultaneous Windows and macOS delivery, superseding only ADR-002's manual operating-system hotspot-selection step.
- Renderer uses the existing typed Host seam and never invokes native networking, arbitrary IPC, shell commands, interface names, SSIDs, BSSIDs, profiles, or URLs directly.
- Renderer receives bounded `Xiaozhi-*` candidate projections with short-lived opaque candidate IDs. Dialog reset immediately drops all Renderer references; Main invalidates the snapshot on a later scan/explicit clear, after the 60-second TTL, or on process restart.
- Only open Robot hotspots may be connected. Makelore does not accept or retain hotspot/home-network passwords and does not become a general Wi-Fi manager.
- Discovery and connection are convenience signals, not authenticated Robot identity, account claim, Binding, or online readiness.
- Native operations are bounded, non-overlapping, locally handled before Works credentials/upstream access, and fail safely to the existing system-settings/manual path.
- Windows and macOS use separate platform Adapters behind the same deep Module interface; tests replace those Adapters rather than exposing platform details through the Renderer contract.
## Project Context Loaded
Task context:
- Task ID: `20260816-robot-hotspot-connect-5e7a91`
- Mode: Feature
- Branch: `codex/20260816-robot-hotspot-connect-5e7a91-robot-hotspot-connect`
- Worktree: `D:\Datas\OthersProjects\makelore-robot-hotspot-connect-5e7a91`
- Base commit: `abecd5f34485ab467e5f032c618083d88e34b74d`
- Other active local tasks: all owners returned by `task_context.py status --json` were checked through their task-record Scope, Intent And Constraints, and Promotion Candidates only.
- Overlap or semantic-conflict assessment: completed Robot onboarding/default-on tasks overlap semantically but do not own this worktree or an in-app Wi-Fi Module. ADR-002 conflicts with automatic discovery, but the user has explicitly confirmed the superseding Windows+macOS product decision. No unresolved peer decision blocks implementation.
Read:
- `.project-docs/05-agent-entry/read-before-planning.md`
- `.project-docs/05-agent-entry/planning-gate.md`
- `.project-docs/05-agent-entry/memory-index.md`
- `.project-docs/30-worklog/tasks/20260816-robot-hotspot-connect-5e7a91.md`
- `.project-docs/00-brief/project-positioning.md`
- `.project-docs/00-brief/success-criteria.md`
- `.project-docs/30-worklog/current-state.md`
- `.project-docs/10-decisions/decision-index.md`
- `.project-docs/10-decisions/adr-002-robot-guided-hotspot-binding-v1.md`
- `.project-docs/20-architecture/system-overview.md`
- `.project-docs/20-architecture/module-map.md`
- `.project-docs/20-architecture/data-flow.md`
- `.project-docs/40-domain/business-rules.md`
- `.project-docs/40-domain/glossary.md`
- `.project-docs/50-evidence/evidence-index.md`
- `.project-docs/60-reflection/reflection-index.md`
- `.project-docs/80-commitments/commitments.md`
- `.project-docs/90-maintenance/stale-items.md`
Relevant understanding:
- Project goal: cross-platform Electron product with Robot as the single hardware module and Main ownership of native/system integration.
- Current integrated focus: Guided Hotspot Binding is default-on, firmware-zero-change, manual at the OS hotspot step, then fixed portal and six-digit Binding.
- Active task scope: move only Robot hotspot discovery/selection/connection into the dialog for Windows and macOS.
- Active constraints: no firmware or cloud-contract change; no Wi-Fi credentials; no arbitrary native input; preserve safe fallback and exact rollback.
- Decisions affecting this task: ADR-002 is accepted and must be superseded only at `connect_device_ap`; a new decision proposal is required because public workflow and architecture change.
- Evidence, reflections, or commitments affecting this task: exact shipped firmware, physical-device flow, native opener, and full end-to-end evidence remain release gates; local tests cannot claim physical compatibility.
- Files or modules likely involved: `electron/services/robot-hotspot-*`, `electron/api/routes/ai-hardware.ts`, `src/lib/ai-hardware.ts`, `src/pages/AiHardware/index.tsx`, focused Robot tests, packaging/native assets, task-scoped proposal/evidence.
- Unknowns, stale docs, or conflicts: project-positioning remains a placeholder; Windows and macOS physical permission/association behavior cannot be fully verified from the Windows development host; accepted canonical docs still describe manual OS selection until Integration Gate.
Gate result:
- Passed. User confirmation resolves the decision change; implementation remains isolated and preserves all unaffected ADR-002 rules.
## Outcome
- Decision proposal created at `.project-docs/10-decisions/proposals/20260816-robot-hotspot-connect-5e7a91__cross-platform-robot-hotspot-connection.md`.
- Added a Main-owned Robot Hotspot Module with bounded opaque candidate snapshots, stable safe errors, exclusive native operations, open `Xiaozhi-*` filtering, strongest-SSID deduplication, and exact current-SSID verification.
- Added real Windows `wlanapi.dll` and macOS CoreWLAN/CoreLocation Adapters through lazily loaded Koffi 2.16.3; neither Adapter uses a shell, CLI, persistent Windows Wi-Fi profile, arbitrary Renderer SSID, or firmware change.
- macOS CoreWLAN scan, association, and exact-SSID verification run in a dedicated abortable worker that owns all Objective-C objects. Foreground CoreLocation authorization remains in Main; an 8/12-second Module deadline can terminate a hung worker without freezing Electron Main. A deduplicated termination barrier prevents a replacement operation from creating or messaging another worker until the previous worker has fully exited.
- Added strict local Host scan/connect routes and typed Renderer projections. Both routes remain behind Host authentication and the existing `guidedHotspotBinding` capability, and finish before Works token/upstream access.
- Replaced the manual-only hotspot step with automatic in-dialog scan, explicit selection/connection, signal and connected-state display, safe recovery messages, rescan, async-session guards, and a retained system-Wi-Fi/manual fallback.
- Added macOS location usage descriptions and synchronized `README.md` with the Windows/macOS product behavior.
- Integration runtime smoke found and fixed CommonJS dynamic-import interop: Electron exposes Koffi through the `default` export. The corrected adapters now load under Electron 40.10.6.
## Verification
- `pnpm vitest run tests/unit/robot-hotspot-module.test.ts tests/unit/ai-hardware-routes.test.ts tests/unit/ai-hardware-api.test.ts tests/unit/ai-hardware-page.test.tsx`: PASS, 4 files / 132 tests.
- `pnpm test`: PASS on the latest tree, 157 files / 1796 tests.
- `pnpm run typecheck`: PASS.
- `pnpm run lint:check`: PASS with 6 pre-existing warnings and no errors.
- `pnpm run build:vite`: PASS for Renderer, Electron Main, and Preload; existing chunk-size warnings only.
- Electron 40.10.6 runtime probe: Koffi 2.16.3 loaded, `wlanapi.dll` loaded, and the WLAN function definition resolved.
- Windows native scan probe: `WlanOpenHandle` and interface enumeration succeeded; this machine's Windows location setting denied `WlanScan` with native code 5, which the public Module correctly projected as `permission_denied`. No SSIDs were printed or retained by the probe.
- Firmware repository `D:\Datas\HardwareProjects\xiaozhi-esp32-firmware` remains clean at `0449e51dec7e0fad3d4567a7bcfcd354efbe1a87`.
- `git diff --check`: PASS; line-ending notices only.
- Final read-only Sol review: PASS on both Standards and Spec axes with no P0-P3 findings.
## Follow-ups
- Run signed packaged macOS x64 and arm64 smoke for location authorization, CoreWLAN scan, open-hotspot association, current SSID verification, and Koffi load. This cannot be proven from the Windows development host.
- Run a Windows physical Robot smoke with location/Wi-Fi access enabled, including multiple Robot candidates and verified connection to the fixed Portal flow.
- Confirm on signed packaged macOS that the bundled `Worker(__filename)` dynamic Main chunk loads from ASAR and Koffi is worker-thread compatible on both x64 and arm64; the build preserves the worker self-entry, but the Windows host cannot execute it.
- Windows waits a bounded four seconds for scan refresh rather than registering WLAN notifications; revisit notification-based completion only if physical evidence shows stale/slow scan results.
## Promotion Candidates
- Target canonical documents: `10-decisions/decision-index.md`, a new accepted ADR following ADR-002, `20-architecture/system-overview.md`, `20-architecture/module-map.md`, `20-architecture/data-flow.md`, `30-worklog/current-state.md`, `40-domain/business-rules.md`, `40-domain/glossary.md`, and `50-evidence/evidence-index.md` after platform evidence exists.
- Proposal: promote `.project-docs/10-decisions/proposals/20260816-robot-hotspot-connect-5e7a91__cross-platform-robot-hotspot-connection.md`; it supersedes only ADR-002's manual OS hotspot-selection step and preserves the rest of ADR-002.
- Evidence: implementation/test/build results in this task record; Windows Electron/Koffi/WLAN permission-path smoke; macOS and physical Robot evidence remain outstanding and must not be inferred.
- Future impact: support, security copy, packaging, notarization, and hardware acceptance must distinguish unauthenticated hotspot convenience from Robot identity/Binding/online readiness.
- Semantic conflict: accepted ADR-002 still states OS-level manual hotspot selection and must be updated/superseded during Integration Gate. No other accepted decision is intentionally reversed.
- Human confirmation: not required for this product-direction promotion because the user explicitly confirmed in this task that both Windows and macOS should connect from the page. Human or release-owner confirmation is still required before treating incomplete macOS/physical smoke as accepted release evidence.