# Windows Passthrough Reference

Windows 透传用于让 WSL provider 通过 UniDesk 的 Host SSH / WSL SSH 维护桥调用 Windows 侧工具链、驱动和 skill，同时尽量保持一套可维护的 skill 入口。它的目标不是把 Windows 当成新的 provider，而是让 `ssh <PROVIDER_ID>` 进入 WSL 后，可以稳定触达 Windows `cmd.exe`、PowerShell、Python、Node/npm、Keil、COM 串口和 USB 调试器。

## Architecture

标准链路是：本机或计算节点 CLI 调用 `trans <PROVIDER_ID> ...`，main server 的 backend-core 通过 provider WebSocket 下发 SSH open/dispatch 控制消息，终端 stdin/stdout/stderr 数据面走 provider 主动连入 main server 的 `host.ssh.tcp-pool` TCP warm pool，provider-gateway 再用只读挂载的维护私钥连到目标 WSL sshd。进入 WSL 后，Windows-only 工具由 WSL 用户目录下的轻量 wrapper 调用 Windows 可执行文件。

这条链路分三层维护：

- `provider-gateway` 只负责 Host SSH / WSL SSH 维护桥，不直接理解 Keil、串口或业务 skill。
- WSL wrapper 负责路径转换、当前目录转换、Windows 进程启动、UTF-8 输出和 cmd/PowerShell 差异。
- skill wrapper 负责把用户仍然熟悉的 `keil`、`serial-monitor`、`board-comm` 命令映射到正确运行侧，避免维护两套互相分叉的 skill。

## UniDesk Win Route

简单 Windows `cmd.exe` 命令不需要依赖节点侧 `win-cmd` wrapper。UniDesk CLI 内置 `win` route：

```bash
trans <PROVIDER_ID>:win cmd ver
trans <PROVIDER_ID>:win/c/test cmd cd
trans <PROVIDER_ID>:win skills --limit 20
```

`<PROVIDER_ID>:win` 只定位到 WSL provider 的 Windows host cmd 执行面，后面的 `cmd <command-line>` 才是操作。`<PROVIDER_ID>:win/<drive>/<path>` 使用 slash 语法设置 Windows cwd，例如 `D601:win/c/test` 会在 Windows cmd 内执行 `cd /d "C:\test"`。该入口固定在运行用户命令前设置 `chcp 65001>nul`、`set "PYTHONUTF8=1"` 和 `set "PYTHONIOENCODING=utf-8"`，因此中文 stdout/stderr 和 Python 子进程默认按 UTF-8 处理；环境变量必须使用 cmd 引号写法，避免 `set VAR=1 && ...` 把分隔符前的空格写进变量值。

长期命名只允许 `win`，不允许 `win32`。`win` route 仍复用 provider-gateway 的 Host SSH / WSL SSH 维护桥抵达目标 WSL provider，但不会依赖 WSL 登录 shell 的 Windows `PATH`，也不再通过节点侧 `win-cmd` wrapper 二次转发。CLI 固定从 `/mnt/c/Windows` 启动 Windows PowerShell UTF-8 launcher，再由 launcher 执行 `C:\Windows\System32\cmd.exe /d /s /c <command-line>`；PowerShell 只负责把 Console/Input/OutputEncoding 设为 UTF-8 并保留 cmd 的 exit code，用户命令语义仍属于 Windows `cmd.exe`。需要 Keil、串口、Windows Node/npm 或复杂路径转换时，继续使用下文的 skill/wrapper 入口，直到对应能力被纳入 UniDesk 内置 route。

`<PROVIDER_ID>:win skills` 是 Windows 用户 skill 发现入口，默认读取当前 Windows 用户 `%USERPROFILE%\.agents\skills`，输出 JSON 中包含 `roots`、`counts` 和每个 skill 的 `name`、`path`、`skillFile`、`description`。需要同时读取 `%USERPROFILE%\.codex\skills` 时使用 `--scope all`；只看 Codex skill 时用 `--scope codex`。该入口用于替代手写 `cmd dir`、`powershell Get-ChildItem` 或宽泛扫描用户目录。

`trans <PROVIDER_ID>:win ps` 会把用户脚本写入 Windows 临时 `.ps1`，并在脚本前注入 UTF-8 与 JSON 安全 prelude。该 prelude 只在本次脚本作用域内覆盖 `ConvertTo-Json`，先把常见 PowerShell ETS 扩展属性剥离为普通 JSON 值，再调用 `Microsoft.PowerShell.Utility\ConvertTo-Json` 输出；这样 `Get-Content -Tail` 得到的字符串放入 `[pscustomobject]` 后不会把 `PSPath`、`PSDrive`、`PSProvider` 和 provider 类型对象图一起序列化。若确实需要原始 PowerShell cmdlet 行为，可在脚本中显式调用 `Microsoft.PowerShell.Utility\ConvertTo-Json`。

`trans <PROVIDER_ID>:win download <windows-path> <local-path>` 支持 drive-letter Windows 路径的二进制 verified transfer。CLI 会先通过 Windows file-transfer helper 对原始 Windows 路径做 bytes/SHA-256 stat，再把同一文件自动映射为该 provider 的 WSL host 路径 `/mnt/<drive>/...` 走 `host.ssh.tcp-pool` stdout stream 下载，最终返回 `verification.verified=true`、`verification.match.bytes=true` 和 `verification.match.sha256=true`。例如 `trans D518:win download 'D:\tmp\image.png' /tmp/image.png` 不需要人工改写成 `/mnt/d/tmp/image.png`；`trans D518:win/d/tmp download image.png /tmp/image.png` 也会按 route workspace 解析。UNC 路径和没有 drive-letter 的路径无法自动映射，需先复制到可由 WSL `/mnt/<drive>` 访问的位置。

## Windows Long-Lived Process Detach

`trans <PROVIDER_ID>:win cmd ...` 和 `trans <PROVIDER_ID>:win ps ...` 适合短命令、只读探测和有界 skill 调用，不适合直接启动 Windows 长驻进程。Windows `cmd start`、`cmd /c ... &`、PowerShell `Start-Process -PassThru` 或带 stdout/stderr 重定向的子进程，仍可能被 provider-gateway/SSH broker 按子进程树或继承句柄等待；结果是远端进程其实已启动，但 `trans` 会持续占用 provider session，后续 D601/G14 高频调用被 provider session lock 串行排队。

长驻 Windows 进程必须使用明确脱离当前 `trans` 会话的启动模型：

- 优先把启动参数写入节点私有 profile 或 `.cmd`/`.ps1` 文件，再通过 Windows Task Scheduler、Windows Service、NSSM、PM2 Windows service 或同等 supervisor 启动。
- 如果只是临时实验，使用 `schtasks /Create ... /TR "<cmd file>" /SC ONCE ... /F` 后再 `schtasks /Run ...`，并把 stdout/stderr 写入固定日志文件；验证用独立短命令读取 `tasklist`、`Get-CimInstance Win32_Process`、日志尾部和服务健康。
- 不要在 `D601:win cmd` / `D601:win ps` 内用 `start` 或 `Start-Process` 直接启动 `hwlab-gateway`、串口 monitor、Codex app-server、Keil job watcher 等长驻进程；如果误用了并导致 `trans` 卡住，应先停止对应 Windows PID 或本地被卡住的 trans/tran broker 进程，再改为 detached supervisor。
- 启动命令必须避免从 WSL UNC cwd 进入 Windows cmd；长驻进程的 `cwd` 应是 Windows 盘符路径，例如 `F:\Work\HWLAB`，日志也写到同一节点私有 `.state` 或 skill state 目录。
- 长驻进程的验收标准不是“启动命令返回”，而是独立短命令能看到 PID、日志首行、health endpoint 或 cloud-side session/resource/capability 已注册。

该规则尤其适用于 D601 Windows `hwlab-gateway` 连接 G14 HWLAB cloud-api 的场景：gateway 是长期 outbound poll 进程，必须由 detached launcher 或 supervisor 管理；`trans` 只负责创建/更新 launcher、触发启动和读取状态。

## Skill Discovery

先用 UniDesk SSH 透传内置的 skill 发现入口确认目标节点上 WSL 与 Windows 两侧 skill 的实际位置：

```bash
trans <PROVIDER_ID> skills --limit 80
trans <PROVIDER_ID> skills --scope windows --limit 40
bun scripts/cli.ts --main-server-ip <MAIN_SERVER_IP> ssh <PROVIDER_ID> skills --scope wsl --limit 20
```

`ssh <PROVIDER_ID> skills` 输出 JSON，包含 `roots` 和 `skills`。WSL provider 会同时扫描 WSL/Linux 的 `~/.agents/skills`、`~/.codex/skills`，以及 Windows 用户目录下的 `/mnt/c/Users/*/.agents/skills`、`/mnt/c/Users/*/.codex/skills`。不要用宽泛的 `find /mnt/*` 搜 Windows 盘；Windows 挂载层可能因权限、索引或设备状态导致长时间阻塞。

## Wrapper Contract

WSL 用户的 `~/.local/bin` 是推荐 wrapper 放置位置，并应在 SSH 登录 shell 的 `PATH` 中优先于系统路径。wrapper 名称和职责固定如下：

- `win-powershell <powershell-command>`：用于 Windows 发现、WMI/CIM、注册表、设备和 JSON 输出；应设置 UTF-8 输出，并用 `Set-Location -LiteralPath` 进入 Windows 当前目录。
- `win-cmd <cmd-line>`：用于 `.cmd`、`.bat` 或必须走 `cmd.exe` 的 legacy 工具；应先写入 Windows 临时 `.cmd` 文件再执行，避免 UNC 当前目录警告和带空格路径引号错误。
- `win-argpath <arg>`：把 `/mnt/<drive>/...` 或存在的 WSL 路径转换为 Windows 路径，其余参数原样返回。
- `win-py <args...>`：调用 Windows `py.exe -3`，并对路径参数做 `/mnt/<drive>` 到 `X:\...` 的转换。
- `win-node <args...>`：调用 Windows `node.exe`，适合必须运行在 Windows 设备上下文中的 Node 脚本。
- `win-npm <args...>`：调用 Windows `npm.cmd`；涉及 COM 口、USB 设备或 Windows-only npm 依赖时优先使用它。
- `win-skill-path <skill-name>`：在 Windows `.agents/skills` 与 `.codex/skills` 中定位 skill 根目录，供上层 skill wrapper 复用。

`win-cmd` 不应直接从 WSL 默认 UNC cwd 运行 `cmd.exe /C <command>`。稳定做法是选择一个 Windows 可写临时目录，例如 `C:\Temp`，生成 `.cmd` 文件，在脚本中 `chcp 65001`、`cd /d "<Windows cwd>"`，再执行用户命令。这样可同时处理 `C:\Program Files` 这类带空格路径和 Windows 工具不支持 UNC 当前目录的问题。

## Path And CWD Rules

Windows 可执行文件只能稳定访问 Windows 盘符路径。WSL wrapper 应遵循这些规则：

- 当前目录在 `/mnt/<drive>/...` 下时，自动转换为 `<DRIVE>:\...` 作为 Windows cwd。
- 当前目录不在 `/mnt/<drive>` 下时，默认使用 `C:\Windows`，或由调用者显式设置 `WIN_CWD`。
- 参数中存在的 `/mnt/<drive>/...` 路径应转换为 Windows 路径；普通参数、IP、端口、JSON 字符串不应被转换。
- 需要 Windows 工具直接读写的工程仓库应放在 Windows 盘符挂载路径下；WSL 原生 `/home/...` 更适合作为 UniDesk、脚本和临时 Linux 工作区。
- 从 WSL 到 Windows 的搜索优先使用 `win-powershell` 或 `ssh <PROVIDER_ID> skills`，再用结构化 `glob`/`find` 缩小到已知目录。

## Skill Compatibility Strategy

不要为 Windows 和 WSL 分别维护两套业务逻辑分叉的 skill。推荐策略是保留一个用户可见命令入口，在 wrapper 中选择正确运行侧：

- `keil`：依赖 Windows Keil、CMSIS-DAP/ST-Link/J-Link 驱动、注册表和工程路径，WSL 入口应调用 Windows 侧 `keil` skill 的 `keil-cli.py`，通常经 `win-py` 执行。
- `serial-monitor`：访问 Windows COM 口时必须运行在 Windows Node/npm 上，WSL 入口应通过 `win-npm --prefix <windows-skill-dir> run cli -- ...` 调用。
- `board-comm`：当前 JSON-RPC over TCP 不依赖 Windows 驱动，可直接用 WSL Python 运行 WSL 侧 skill；只有未来新增 serial transport 时才需要再评估是否切到 Windows 侧。

工程级 wrapper 可以保留 `*-wsl` 别名，例如 `keil-wsl`、`serial-monitor-wsl`、`board-comm-wsl`，但用户文档中的正式入口仍应优先写 `keil`、`serial-monitor` 和 `board-comm`，避免让任务 prompt 绑定到某台机器的一次性脚本名。

## Skill Modification And Bootstrap Status

Windows 透传机制不要求修改 WSL skill 的业务代码。标准交付边界应区分三类内容：

- UniDesk 内置能力：`ssh <PROVIDER_ID> skills`、`apply-patch`、`glob`、`py` 等 helper 由 UniDesk CLI 在 SSH 会话启动时注入 `/tmp/unidesk-ssh-tools`，属于开箱即用能力；它们不修改远端 skill 目录。
- 节点 bootstrap：`win-cmd`、`win-powershell`、`win-py`、`win-npm`、`win-skill-path` 以及 `keil`、`serial-monitor`、`board-comm` 的 WSL wrapper 属于节点运行环境 bootstrap，通常放在目标 WSL 用户的 `~/.local/bin`；这不是 skill 本身开箱即用的一部分。
- skill 代码：除非明确要把 wrapper 能力合入某个 skill，否则不得为了某台节点直接改 `~/.agents/skills/<skill>` 或 `~/.codex/skills/<skill>` 的业务代码；如果确实修改了 skill CLI 或 `SKILL.md`，必须按该 skill 的测试和文档规则独立验收。

因此，判断一个节点是否“开箱即用”时要分别检查：UniDesk CLI 是否支持 `ssh skills`，目标 WSL 是否已有 wrapper bootstrap，Windows 侧是否已有 Keil/Node/npm/Python/驱动，目标 skill 是否已有依赖。缺少 wrapper 或 Windows 工具链时，应报告为节点环境未 bootstrap 完成，而不是把它误判成 skill 逻辑失败。

当前推荐不在 wrapper 中自动安装依赖。`serial-monitor` 所需 Windows `node_modules`、Keil pack、pyOCD、USB probe 驱动、COM 驱动等都应由节点 bootstrap 或人工安装完成；wrapper 只负责调用和给出明确错误。这样可以避免 agent 在硬件节点上静默安装版本不明的依赖，导致后续验收不可复现。

## Dependency Bootstrap

Windows 透传依赖分为四类：UniDesk 侧能力、WSL 侧基础环境、Windows 侧工具链、skill 本地依赖。它们应由节点 bootstrap 或人工维护显式安装，不应由业务任务中的 wrapper 静默安装。

### UniDesk And WSL Prerequisites

WSL provider 至少需要：

- WSL sshd 已启动，provider-gateway 容器可用维护私钥免密登录目标 WSL 用户。
- 目标 WSL 用户 `PATH` 包含 `~/.local/bin`，用于放置 `win-*` 与 skill wrapper。
- WSL 内有 `python3`、`base64`、`bash`、`wslpath`；UniDesk SSH 注入的 `py`、`apply-patch`、`glob` 和 `skill-discover` 依赖这些基础命令。
- WSL 可访问 Windows 盘符挂载，例如 `/mnt/c`、`/mnt/d`、`/mnt/f`。
- 如果 provider-gateway 跑在 Docker Desktop daemon 上，容器到 WSL sshd 的 `host.docker.internal:22` 链路必须可用。

WSL wrapper bootstrap 至少应安装或生成这些脚本：

```bash
mkdir -p ~/.local/bin
# win-cmd, win-powershell, win-argpath, win-py, win-node, win-npm, win-skill-path
# keil, serial-monitor, board-comm wrappers
chmod +x ~/.local/bin/win-* ~/.local/bin/keil ~/.local/bin/serial-monitor ~/.local/bin/board-comm
```

这些 wrapper 属于节点环境，不应提交到 UniDesk 仓库，除非后续专门新增一个受版本管理的 provider bootstrap 脚本。

### Windows Toolchain Prerequisites

Windows 侧至少需要按目标硬件场景安装：

- Python Launcher：`py.exe` 可用，且 `py -3` 指向 Python 3。
- Node.js/npm：`node.exe` 和 `npm.cmd` 可用；涉及 COM 口的 TypeScript skill 应运行在 Windows Node 上。
- Keil MDK：`UV4.exe` 可用，Keil Pack 已安装到工程需要的 MCU 系列。
- pyOCD：Windows Python 环境中可用 `python -m pyocd`，用于 CMSIS-DAP 探头检测和 pyOCD backend 下载。
- USB 调试器驱动：CMSIS-DAP、DAPLink、ST-Link 或 J-Link 对应驱动可被 Windows 识别。
- 串口驱动：目标板 UART bridge 在 Windows 设备管理器中显示为 `COMx`。
- PowerShell：用于 WMI/CIM 设备发现、路径诊断和 Windows 侧环境检查。

推荐的只读检查命令：

```bash
win-powershell "Get-Command py.exe,node.exe,npm.cmd -ErrorAction SilentlyContinue | Select-Object Name,Source | ConvertTo-Json"
win-powershell "Get-CimInstance Win32_SerialPort | Select-Object DeviceID,Name,Description,PNPDeviceID | ConvertTo-Json -Depth 3"
win-cmd "where py && where node && where npm"
```

Keil 与 pyOCD 检查应通过 skill 入口完成：

```bash
keil status
keil list-devices -p <project.uvprojx>
```

### Skill Dependencies

各 skill 的依赖安装边界如下：

- `keil`：Windows 侧 `~/.agents/skills/keil` 应已存在 `keil-cli.py`、`config.json` 与所需 Python 依赖；Windows Python 中必须能导入 pyOCD。Keil pack 和 probe 驱动属于 Windows 工具链依赖，不属于 WSL wrapper 自动安装范围。
- `serial-monitor`：Windows 侧 `~/.agents/skills/serial-monitor` 需要已执行过 `npm install`，生成对应 Windows 平台的 `node_modules`。从 WSL 调用时应使用 `win-npm --prefix <windows-skill-dir> run cli -- ...`，不要复用 WSL 的 `node_modules` 去访问 Windows COM 口。
- `board-comm`：JSON-RPC over TCP 可直接使用 WSL 侧 `~/.agents/skills/board-comm` 与 WSL Python；如果 `check` 因缺少 pyright 失败，但 `jrpctcp` 正常，不应阻塞硬件通信验证。若未来增加 serial transport，再按运行侧补充 Windows 依赖。

依赖安装必须显式、可复现，并优先在对应 skill 的 `SKILL.md` 或节点 bootstrap 文档中记录。业务任务中如果发现依赖缺失，应输出缺失项、建议安装命令和验证命令，而不是直接静默安装。

### Verification Matrix

节点完成依赖 bootstrap 后，至少运行以下验证：

```bash
trans <PROVIDER_ID> skills --limit 20
trans <PROVIDER_ID> -- 'export PATH="$HOME/.local/bin:$PATH"; command -v win-cmd win-powershell win-py win-npm keil serial-monitor board-comm'
trans <PROVIDER_ID> -- 'export PATH="$HOME/.local/bin:$PATH"; win-py -V; win-npm -v'
trans <PROVIDER_ID> -- 'export PATH="$HOME/.local/bin:$PATH"; keil status'
trans <PROVIDER_ID> -- 'export PATH="$HOME/.local/bin:$PATH"; serial-monitor ports'
trans <PROVIDER_ID> -- 'export PATH="$HOME/.local/bin:$PATH"; board-comm debug build-jrpctcp-request get api'
```

硬件项目级验收还应覆盖真实 `build`、`program`、串口抓取和 `board-comm jrpctcp get api`。如果只有依赖检查通过但没有真实硬件闭环，不能宣称该工程已完成下载和通信验收。

## Hardware Workflow

Keil/串口/board-comm 的通用顺序如下：

1. `ssh <PROVIDER_ID> skills` 确认 WSL 与 Windows skill 位置。
2. `win-powershell` 或 `serial-monitor ports` 确认可见 COM 口。
3. `keil status` 和 `keil list-devices -p <project.uvprojx>` 确认 Keil、pyOCD 和 USB probe。
4. `keil build --wait -p <project.uvprojx> -t <target>` 构建。
5. `serial-monitor server start --force` 和 `serial-monitor monitor start -p <COMx> -b <baud>` 先打开串口。
6. `keil program --wait ... -u <probe_uid>` 下载并运行。
7. `serial-monitor fetch --session-only --no-dedup -l <N>` 抓取串口证据。
8. `board-comm jrpctcp --host <board_ip> --port <port> get api` 验证主动通信面。

多 probe 同时在线时，`keil program`、`keil detect` 必须显式传 `-u <probe_uid>`。若 Keil UV4 backend 报缺少 flash/download metadata，可优先用 pyOCD backend 完成下载；是否补齐 UV4 工程下载配置应作为工程维护问题处理，而不是 SSH/Windows 透传问题。

## Code Queue Windows Native Codex

Code Queue 支持在提交任务时选择 `executionMode=windows-native`。该模式用于 D601 这类 WSL Provider：D601 Code Queue 先通过本节点执行环境建立任务运行环境，但容器内不启动 `codex`；容器只运行 stdio relay，连接到 WSL 侧 bridge，再由 bridge 调用 Windows 宿主原生 `codex app-server --listen stdio://`。这样 UniDesk 控制面到 Provider 的临时断连不会直接终止 Windows Codex 进程。

约束：

- 只支持非主 server WSL Provider 和 Codex 模型；`minimax-m3` / `minimax-m2.7` / OpenCode port 不走该模式。
- 工作目录必须在 `/mnt/<drive>/...` 下，供 `win-cmd` 转换为 Windows 盘符 cwd；D601 默认提示为 `/mnt/f/Work/ConStart`。
- Windows 侧必须已安装 `codex`，且 WSL wrapper `win-cmd` 可用；可用 `trans D601 -- 'export PATH="$HOME/.local/bin:$PATH"; win-cmd "where codex && codex --version"'` 验证。
- 任务 JSON、列表、Trace 摘要和前端卡片必须显示 `executionMode`，便于区分默认容器 Codex 与 Windows 原生 Codex。

## Remote Frontend Limits

`--main-server-ip ... ssh <PROVIDER_ID> <command>` 走 frontend 登录态和 `host.ssh` dispatch，适合短命令和 skill discovery。它不流式转发 stdin，也受 provider-gateway 的 host.ssh command length 限制；`apply-patch`、`py < stdin`、超长 inline 脚本和需要完整终端流的操作应在 main server 本机 CLI 上执行，或显式走旧 SSH transport。

`ssh <PROVIDER_ID> skills` 在 remote frontend 模式下使用短 inline 发现脚本以避开命令长度限制；如果输出被任务历史截断，应改在 main server 本机 CLI 执行同一命令以获得完整 stdout。

## Safety Rules

- Windows 透传只用于节点诊断、硬件工具链和人工维护，不得作为 provider-gateway 自重建通道；provider-gateway 升级仍必须走 `provider.upgrade mode=schedule`。
- 不要把 Windows 透传 wrapper 写入业务仓库根目录；它们属于节点运行环境，应放在 WSL 用户目录或节点私有 bootstrap 脚本中。
- 不要在 wrapper 中静默安装依赖；缺少 `py.exe`、Node/npm、Keil、COM 驱动或 probe 驱动时，应输出明确错误并让节点 bootstrap 修复。
- 不要把 Windows 用户目录、串口日志、Keil 日志、SSH key 或 provider token 提交到 UniDesk 仓库。
- 串口抓取和烧录会影响真实硬件状态；执行前应确认目标 project、target、probe UID、COM 口和 baud rate。
