441 lines
24 KiB
Markdown
441 lines
24 KiB
Markdown
# Device Pod 正式接入规格
|
||
|
||
本文是 HWLAB `v0.2` 正式接入 `device-pod` 的规格说明。`device-pod` 是一个逻辑设备能力单元,不是 Kubernetes Pod 名称,也不是 code agent 本地 profile 文件。正式接入后,profile 定义 `device-pod`,因此 profile 必须由管理员和服务端权威存储管理,不能由 code agent 本地文件决定路由或资源边界。
|
||
|
||
实施跟踪见 [pikasTech/HWLAB#533](https://github.com/pikasTech/HWLAB/issues/533),原 `docs/plan/v02-device-pod-spec-migration.md` 和旧 device-pod MVP 计划全文已迁入该 issue 评论。
|
||
|
||
旧的 `device-pod-cli` 本地 profile 闭环只用于 CLI MVP 和真实硬件最小验证。进入正式多用户系统后,所有用户态设备访问必须收敛到:
|
||
|
||
```text
|
||
device-pod-cli or cloud-web
|
||
-> cloud-api auth + device_pod_grants + lease
|
||
-> hwlab-device-pod internal REST
|
||
-> gateway transport
|
||
-> device-host-cli
|
||
-> Keil / pyOCD / UART / target
|
||
```
|
||
|
||
## 在系统中的职责划分
|
||
|
||
`device-pod` 是云端可授权、可租约保护、可审计的逻辑设备能力单元。`hwlab-cloud-api` 是用户身份、grant、lease、profile authority 和用户态 REST API 的入口;`hwlab-device-pod` 是内部执行服务;`hwlab-gateway` 只承载 transport;`device-host-cli` 只在硬件 host 侧执行 Keil、pyOCD、UART 和 workspace 操作。
|
||
|
||
普通用户、浏览器和 Code Agent session 不直接持有 gateway route、host workspace route、Kubernetes Service 直连能力或 profile 修改权。
|
||
|
||
## 设计目标
|
||
|
||
- 用最少组件把 `device-pod-cli` 从“本地 profile + RPC/gateway 调用”迁到“1:1 REST 请求”。
|
||
- `cloud-api` 是用户身份、device grant、profile authority 和 lease 判断入口。
|
||
- `hwlab-device-pod` 承接设备业务:profile 校验后的运行、job 生命周期、freshness、blocker、bounded output 和 gateway 调用。
|
||
- `device-pod-cli` 只做 selector 解析、cloud-api REST 请求和 JSON 输出;默认正式模式不读取 `.device-pod/*.json`,不保存、不上传、不修改权威 profile。
|
||
- 第一阶段只部署一个 `hwlab-device-pod` Deployment/Service,管理多个逻辑 `devicePodId`,避免为每台设备创建独立 k8s Service/Deployment。
|
||
- 普通用户和 code agent session 不获得 Kubernetes 用户、Service 直连权限、gateway route 或 host workspace route。
|
||
|
||
## 逻辑模型
|
||
|
||
一个 `device-pod` 由四个设备能力要素组成:
|
||
|
||
```text
|
||
device-pod
|
||
= deviceTarget
|
||
+ debugInterface
|
||
+ projectWorkspace
|
||
+ ioInterface
|
||
```
|
||
|
||
- `deviceTarget`:被测设备目标,例如开发板、用户 PCB 或仪器模块。
|
||
- `debugInterface`:下载、复位、chip-id、probe 状态和调试连接能力。
|
||
- `projectWorkspace`:源码、工程、构建工具链和 artifact 边界。
|
||
- `ioInterface`:UART、DI/DO、采样、日志和其他设备 I/O 观测/控制能力。
|
||
|
||
`devicePodId` 是云端和用户界面的稳定身份。实际 k8s Pod 可以重建、滚动或扩容;用户和 code agent 不依赖实际 Pod name。
|
||
|
||
## Profile Authority
|
||
|
||
正式接入后,profile 是管理员侧资源:
|
||
|
||
```text
|
||
admin UI/API
|
||
-> cloud-api
|
||
-> device_pods.profile_json + profile_hash
|
||
-> hwlab-device-pod internal execution
|
||
```
|
||
|
||
code agent 本地文件只能作为非权威 hint/cache,最多包含:
|
||
|
||
```json
|
||
{
|
||
"devicePodId": "device-pod-71-freq",
|
||
"profileHash": "sha256:...",
|
||
"cloudApiUrl": "..."
|
||
}
|
||
```
|
||
|
||
本地 hint/cache 不得包含以下字段,也不得参与授权或执行路由:
|
||
|
||
- `gatewaySessionId`
|
||
- `resourceId`
|
||
- `capabilityId`
|
||
- `hostWorkspaceRoot`
|
||
- `hostCli`
|
||
- Windows workspace 路径
|
||
- probe UID、串口端口、Keil 路径等硬件路由字段
|
||
|
||
正式 profile 必须由 `cloud-api` 从 DB 读取;`hwlab-device-pod` 不接受浏览器、code agent 或 CLI 上传的 profile 作为执行依据。若 `hwlab-device-pod` 需要 profile snapshot,应只接受 `cloud-api` 内部服务凭据转发的 snapshot,或通过内部服务凭据向 `cloud-api` 拉取。该凭据不得挂载进 code agent session Pod。
|
||
|
||
## 内部架构
|
||
|
||
正式 device-pod 由 profile registry、job lifecycle、freshness/blocker、bounded output、gateway/device-host adapter 和 lease integration 组成。第一阶段只有一个 `hwlab-device-pod` Deployment 管理多个 `devicePodId`;profile authority 和 user grant 在 cloud-api/Postgres 中,device-pod 服务只接受 cloud-api 内部调用。
|
||
|
||
当前 v02 部署中的 `hwlab-device-pod` 微服务实现情况见 [spec-v02-hwlab-device-pod-service.md](spec-v02-hwlab-device-pod-service.md)。
|
||
|
||
## Profile Shape
|
||
|
||
`device_pods.profile_json` 至少表达以下 server-side 字段:
|
||
|
||
```json
|
||
{
|
||
"schemaVersion": 1,
|
||
"devicePodId": "device-pod-71-freq",
|
||
"target": {
|
||
"id": "target-id"
|
||
},
|
||
"projectWorkspace": {
|
||
"workspaceRoot": "F:\\Work\\Project",
|
||
"projectPath": "FirmWare/MDK-ARM/app.uvprojx",
|
||
"targetName": "app",
|
||
"hexPath": "FirmWare/MDK-ARM/app/app.hex"
|
||
},
|
||
"debugInterface": {
|
||
"type": "cmsis-dap",
|
||
"probeUid": "...",
|
||
"uv4Path": "C:\\Keil_v5\\UV4\\UV4.exe"
|
||
},
|
||
"ioInterface": {
|
||
"uart": [
|
||
{ "id": "uart/1", "port": "COM4", "baudRate": 921600 }
|
||
]
|
||
},
|
||
"route": {
|
||
"gatewaySessionId": "gws_...",
|
||
"resourceId": "res_...",
|
||
"capabilityId": "cap_...",
|
||
"hostWorkspaceRoot": "F:\\Work\\Project",
|
||
"hostCli": "node tools\\device-host-cli.mjs"
|
||
}
|
||
}
|
||
```
|
||
|
||
`profile_json` 不得保存 Git key、云 token、kubeconfig、数据库 URL 或长期 secret。`profile_hash` 由 `cloud-api` 对规范化 profile JSON 计算并在所有响应中返回;用户可见响应只能返回脱敏 profile 摘要和 hash。
|
||
|
||
## 数据表口径
|
||
|
||
正式规格推荐 `device_pods` 直接保存权威 profile 和 hash,避免额外 profile 微服务:
|
||
|
||
```sql
|
||
CREATE TABLE IF NOT EXISTS device_pods (
|
||
id TEXT PRIMARY KEY,
|
||
name TEXT NOT NULL DEFAULT '',
|
||
status TEXT NOT NULL DEFAULT 'active' CHECK (status IN ('active', 'disabled')),
|
||
profile_json TEXT NOT NULL DEFAULT '{}',
|
||
profile_hash TEXT NOT NULL DEFAULT '',
|
||
created_at TEXT NOT NULL,
|
||
updated_at TEXT NOT NULL
|
||
);
|
||
```
|
||
|
||
历史版本中的 `profile_ref`、`gateway_ref` 或 `device_pod_json` 可以在迁移时折叠进 `profile_json`。第一阶段不新增 `device_pod_profile_revisions`;需要审计版本、回滚或多环境批准时再引入 profile revision 表。
|
||
|
||
`device_pod_grants` 仍只表达用户是否拥有完整使用权;它不保存 profile,也不拆 capability。
|
||
|
||
## REST API
|
||
|
||
## API 接口说明
|
||
|
||
用户态 API 只经过 `cloud-api` 暴露:
|
||
|
||
```text
|
||
GET /v1/device-pods
|
||
GET /v1/device-pods/{devicePodId}/status
|
||
GET /v1/device-pods/{devicePodId}/debug-probe/chip-id
|
||
GET /v1/device-pods/{devicePodId}/io-probe/uart/1
|
||
GET /v1/device-pods/{devicePodId}/io-probe/uart/1/tail?maxBytes=12000
|
||
POST /v1/device-pods/{devicePodId}/jobs
|
||
GET /v1/device-pods/{devicePodId}/jobs/{jobId}
|
||
GET /v1/device-pods/{devicePodId}/jobs/{jobId}/output
|
||
POST /v1/device-pods/{devicePodId}/jobs/{jobId}/cancel
|
||
POST /v1/device-pods/{devicePodId}/leases
|
||
GET /v1/device-pods/{devicePodId}/leases/current
|
||
DELETE /v1/device-pods/{devicePodId}/leases/current
|
||
```
|
||
|
||
管理员 API 由 `cloud-api` 提供:
|
||
|
||
```text
|
||
POST /v1/admin/device-pods
|
||
PUT /v1/admin/device-pods/{devicePodId}
|
||
POST /v1/admin/device-pod-grants
|
||
DELETE /v1/admin/device-pod-grants/{devicePodId}/{userId}
|
||
```
|
||
|
||
`POST /v1/device-pods/{devicePodId}/jobs` 用 `intent` 表达具体业务,避免把 REST surface 扩张成大量一次性 route:
|
||
|
||
```json
|
||
{
|
||
"intent": "workspace.build",
|
||
"args": { "profile": "debug" },
|
||
"reason": "DEV smoke"
|
||
}
|
||
```
|
||
|
||
第一阶段 intent 集合:
|
||
|
||
- `workspace.ls`
|
||
- `workspace.bootsharp`
|
||
- `workspace.cat`
|
||
- `workspace.rg`
|
||
- `workspace.apply-patch`
|
||
- `workspace.put`
|
||
- `workspace.rm`
|
||
- `workspace.rmdir`
|
||
- `workspace.keil`
|
||
- `workspace.build`
|
||
- `debug.status`
|
||
- `debug.chip-id`
|
||
- `debug.download`
|
||
- `debug.reset`
|
||
- `io.ports`
|
||
- `io.uart.read`
|
||
- `io.uart.read-after-launch-flash`
|
||
- `io.uart.write`
|
||
- `io.uart.jsonrpc`
|
||
|
||
`GET /debug-probe/chip-id`、`GET /io-probe/uart/1` 和 `GET /io-probe/uart/1/tail` 是用户态便捷 REST surface,但不能停留在静态面板或 fake probe;cloud-api 必须在完成 authenticate/grant 后创建对应只读 job,再经 `hwlab-device-pod` executor/gateway/device-host-cli 执行或返回同一套 blocker。
|
||
|
||
所有 job/status/output 和 probe GET 响应必须包含 `devicePodId`、`targetId`、`profileHash`、`traceId`、`operationId`、`status`、`freshness`、`blocker` 和 bounded output metadata。job output 文本默认最大 12000 bytes;超出时必须设置 `truncation.truncated=true`、`truncation.originalBytes`,并避免把完整 executor/gateway 原始输出嵌回 JSON。真实硬件响应不得把 fake、dry-run、SOURCE、LOCAL 或过期缓存标为 `DEV-LIVE`。
|
||
|
||
## 微服务职责
|
||
|
||
| 服务 | 职责 |
|
||
| --- | --- |
|
||
| `hwlab-cloud-api` | 用户身份、admin/user、device grant、lease、profile authority、用户态 REST API、转发到内部 device-pod。 |
|
||
| `hwlab-device-pod` | 多 `devicePodId` 运行 registry、profile runtime validation、job store、freshness、bounded output、gateway/device-host-cli adapter。 |
|
||
| `device-pod-cli` | 把 `devicePodId:surface:path operation args` 1:1 转成 cloud-api REST;不保存权威 profile、不读取本地 profile 作为默认 authority、不直连 gateway。 |
|
||
| `device-host-cli` | Windows host 侧自包含业务工具,负责 Keil、pyOCD、UART、workspace 文件操作。 |
|
||
| `hwlab-gateway` | 只做受控 transport,不理解用户权限和 device-pod 授权。 |
|
||
| `hwlab-cloud-web` | 展示用户可见 device pod、admin 管理 profile/grant、显示 job/status/freshness。 |
|
||
|
||
## Kubernetes 口径
|
||
|
||
v0.2 第一阶段使用一个 `hwlab-device-pod` Deployment 和一个 ClusterIP Service:
|
||
|
||
```text
|
||
hwlab-v02/hwlab-device-pod
|
||
replicas: 1
|
||
manages: many devicePodId
|
||
```
|
||
|
||
不为每个 `devicePodId` 创建 Deployment、Service、Ingress、Secret 或 namespace。这样更符合当前规模:运维对象少、GitOps diff 少、问题定位简单,也不会把设备数量直接放大成 k8s 资源数量。
|
||
|
||
只有在满足以下条件时,才考虑拆分为多个 `hwlab-device-pod` shard 或 per-device workload:
|
||
|
||
- 单个服务内 job 队列和 freshness 监控互相影响;
|
||
- 不同设备需要不同 host network、USB、Secret 或资源 request;
|
||
- 设备数量增长到单实例状态管理明显吃力;
|
||
- 强隔离需求超过应用层 grant 和内部服务凭据能覆盖的范围。
|
||
|
||
普通用户和 code agent session Pod 不应直接调用 `hwlab-device-pod` Service。正式路径是 `code agent -> cloud-api -> hwlab-device-pod`。
|
||
|
||
## 验收标准
|
||
|
||
- `device-pod-cli` 在正式模式下不读取 `.device-pod/<devicePodId>.json` 作为权威 profile,只向 cloud-api 提交 `devicePodId`、intent 和 args。
|
||
- 普通用户无授权时不能看到或使用任何 device pod;授权后拥有对应 device pod 的完整使用权。
|
||
- code agent 不能通过修改本地文件改变 gateway session、resource、host workspace、probe UID 或 UART port。
|
||
- `hwlab-device-pod` 不接受无内部服务凭据的 profile snapshot 或 job 请求。
|
||
- `hwlab-device-pod` 一个实例可以列出并执行多个 `devicePodId` 的状态/job。
|
||
- cloud-api compatibility fallback 只能返回 blocked authority payload,不得合成 fake device pod 数据或作为正式 device-pod DEV-LIVE 证据。
|
||
- 强副作用 job 必须有 reason,并在物理互斥需要时获取 `device_leases`。
|
||
- `POST /v1/device-pods/{devicePodId}/leases` 只对已授权 actor 创建或刷新互斥租约,响应只返回一次性 `leaseToken`;后续强副作用 job 必须通过 `leaseToken` 或 `x-hwlab-device-lease-token` 证明持有租约。
|
||
- 撤销 device pod grant 必须释放该用户对同一 `devicePodId` 的活动 lease。
|
||
|
||
## CLI 实现口径
|
||
|
||
`tools/device-pod-cli.ts` 是 v0.2 正式 CLI 实现;HWLAB code-agent runner 内的稳定短入口是 `hwpod`。`/app/skills/device-pod-cli/scripts/device-pod-cli.mjs` 只作为兼容启动器,在 runner PATH 缺少 `hwpod` 时临时 fallback,并应记录为 runner image/package 摩擦点。正式 CLI 的默认行为是:
|
||
|
||
- `profile list/show` 调用 cloud-api `/v1/device-pods` 和 `/status`,只显示服务端脱敏 profile 摘要和 `profileHash`。
|
||
- `setup first-admin` 和 `admin device-pod upsert/grant` 只作为 cloud-api REST wrapper,用于首次空库 seed 或 admin profile/grant 管理;它们接受显式 `--profile-json` 或 `--device-pod-json`,不得读取本地 `.device-pod/*.json` 作为权威 profile。
|
||
- `devicePodId:workspace|debug-probe|io-probe...` selector 转换为 `POST /v1/device-pods/{devicePodId}/jobs` 或 job status/output/cancel REST,不直接调用 `/v1/rpc/hardware.invoke.shell`。
|
||
- `bootsharp --pod-id <devicePodId>` 和 `<devicePodId>:workspace:/ bootsharp` 都转换为 `workspace.bootsharp` job,用于返回 workspace tree、AGENTS.md 摘要和当前路径提示;该入口是上下文恢复和 DS 派单的首个探测动作,不读取本地 profile。
|
||
- workspace 写操作覆盖 `apply-patch`、`put`、`rm`、`rmdir`、`build` 和 `keil add-source/remove-source` 等 Keil 工程维护动作。
|
||
- 源码局部编辑优先使用 `apply-patch`;`put` 只用于明确的整文件写入或新文件创建。
|
||
- UART 业务覆盖 `read`、`write`、`read-after-launch-flash` 和 `jsonrpc`;JSON-RPC 请求必须由 device-host-cli 校验 response id,除非显式传入允许 id mismatch 的业务参数。
|
||
- apply-patch 类失败必须返回可定位的 patch hint,例如缺少 `*** End Patch`、hunk 上下文不匹配或 header 错误;调用方应先重新读取目标文件再重试小 hunk,不应默认绕到整文件覆盖。
|
||
- mutating job 继续由 cloud-api 侧强制 lease/reason;CLI 只转发 `reason` 和 `leaseToken`,不在本地绕过。
|
||
- `profile create` 这类本地 profile bootstrap 在正式默认路径中返回 `legacy_profile_create_removed`;管理员应使用 cloud-api admin API 管理服务端 profile/grant。
|
||
- DS/device-pod prompt 和 skill 示例必须优先使用 `hwpod`,避免把长路径 wrapper 复制成常态命令;host job 轮询时 job id 必须作为 `status/output/cancel` 的紧随位置参数传入,不能放到 flags 后面或靠 shell 管道解析 JSON。
|
||
|
||
## D601 F103 v0.2 Gateway SOP
|
||
|
||
D601 Windows F103 gateway 的稳定命名使用 `gws_D601_F103`。不要因为当前 `devicePodId` 或样例设备名是 `device-pod-71-freq` / `71-FREQ`,把 gateway session 改成旧的 `gws_d601_win_71_freq`;profile route 和 Windows gateway 运行脚本必须使用同一组 F103 命名。
|
||
|
||
当前正式接入的 F103 v2 逻辑设备名为 `D601-F103-V2`。它的 Windows workspace 固定为 `F:\Work\D601-HWLAB`,Keil 工程为 `projects/01_baseline/Projects/MDK-ARM/atk_f103.uvprojx`,Keil target 为 `USART`,Keil 可执行文件为 `C:\Keil_v5\UV4\UV4.exe`,UART 为 `COM9`/`115200`。v0.2 服务端 profile 必须使用 `devicePodId=D601-F103-V2`,不得继续把该 workspace 暴露成 `device-pod-71-freq`。
|
||
|
||
Windows 侧固定入口:
|
||
|
||
```text
|
||
Task Scheduler: HWLAB-DevicePodGateway-D601-F103
|
||
run script: C:\Users\liang\device-pod-gateway-rust\run-D601-F103.cmd
|
||
local status: http://127.0.0.1:7001/status
|
||
cloud API: http://74.48.78.17:19667
|
||
```
|
||
|
||
`run-D601-F103.cmd` 中的关键环境变量应保持为:
|
||
|
||
```cmd
|
||
set "HWLAB_GATEWAY_CLOUD_URL=http://74.48.78.17:19667"
|
||
set "HWLAB_ENVIRONMENT=v02"
|
||
set "HWLAB_GATEWAY_ID=gtw_D601_F103"
|
||
set "HWLAB_GATEWAY_SESSION_ID=gws_D601_F103"
|
||
set "HWLAB_GATEWAY_RESOURCE_ID=res_windows_host"
|
||
set "HWLAB_GATEWAY_BOX_ID=box_windows_host"
|
||
set "HWLAB_GATEWAY_CMD_CAPABILITY_ID=cap_windows_cmd_exec"
|
||
set "HWLAB_GATEWAY_CMD_EXEC_ENABLED=1"
|
||
set "HWLAB_GATEWAY_MAX_INFLIGHT=4"
|
||
set "HWLAB_GATEWAY_CMD_TIMEOUT_MS=120000"
|
||
set "HWLAB_GATEWAY_PORT=7001"
|
||
```
|
||
|
||
v0.2 `D601-F103-V2` 的服务端 profile route 必须和 gateway 注册保持一致:
|
||
|
||
```json
|
||
{
|
||
"gatewaySessionId": "gws_D601_F103",
|
||
"resourceId": "res_windows_host",
|
||
"capabilityId": "cap_windows_cmd_exec",
|
||
"hostWorkspaceRoot": "F:\\Work\\D601-HWLAB",
|
||
"hostCli": "node tools\\device-host-cli.mjs"
|
||
}
|
||
```
|
||
|
||
重启 gateway 时使用计划任务,不从临时 shell 直接启动长期进程:
|
||
|
||
```cmd
|
||
schtasks /End /TN HWLAB-DevicePodGateway-D601-F103
|
||
schtasks /Run /TN HWLAB-DevicePodGateway-D601-F103
|
||
powershell -NoProfile -Command "Invoke-RestMethod http://127.0.0.1:7001/status | ConvertTo-Json -Depth 12"
|
||
```
|
||
|
||
通过 UniDesk Windows route 操作时,工作目录应直接定位到 gateway 目录,例如 `D601:win/c/Users/liang/device-pod-gateway-rust`,再读取 `run-D601-F103.cmd` 或查询 `/status`。`/status` 中至少应看到 `gatewaySessionId=gws_D601_F103`、`cloudUrl=http://74.48.78.17:19667`、`session.status=connected`、`outbound.lastPollError=null`。
|
||
|
||
G14 v0.2 验收在 `G14:/root/hwlab-v02` 执行,先确认 Cloud Web 同源 CLI 能看到 device pod 状态,再用正式 `device-pod-cli` 创建并轮询 job:
|
||
|
||
```bash
|
||
bun tools/hwlab-cli/bin/hwlab-cli.ts client device-pods status device-pod-71-freq \
|
||
--base-url http://74.48.78.17:19666 \
|
||
--full
|
||
|
||
COOKIE=$(node -pe 'require("./.state/hwlab-cli/session.json").cookie')
|
||
bun tools/device-pod-cli.ts device-pod-71-freq:workspace:/ ls \
|
||
--api-base-url http://74.48.78.17:19667 \
|
||
--cookie "$COOKIE" \
|
||
--reason "v02 gws_D601_F103 smoke" \
|
||
--timeout-ms 120000 | tee /tmp/hwlab-device-pod-job.json
|
||
|
||
JOB=$(node -e 'const fs=require("fs"); const payload=JSON.parse(fs.readFileSync("/tmp/hwlab-device-pod-job.json", "utf8")); console.log(payload.body.job.id)')
|
||
|
||
bun tools/device-pod-cli.ts job status --pod-id device-pod-71-freq "$JOB" \
|
||
--api-base-url http://74.48.78.17:19667 \
|
||
--cookie "$COOKIE"
|
||
|
||
bun tools/device-pod-cli.ts job output --pod-id device-pod-71-freq "$JOB" \
|
||
--api-base-url http://74.48.78.17:19667 \
|
||
--cookie "$COOKIE"
|
||
```
|
||
|
||
`D601-F103-V2` 的验收命令使用同一条 v0.2 CLI 链路,只替换 `devicePodId` 并从 `bootsharp` 开始:
|
||
|
||
```bash
|
||
bun tools/hwlab-cli/bin/hwlab-cli.ts client device-pods status D601-F103-V2 \
|
||
--base-url http://74.48.78.17:19666 \
|
||
--full
|
||
|
||
COOKIE=$(node -pe 'require("./.state/hwlab-cli/session.json").cookie')
|
||
bun tools/device-pod-cli.ts bootsharp --pod-id D601-F103-V2 \
|
||
--api-base-url http://74.48.78.17:19667 \
|
||
--cookie "$COOKIE"
|
||
|
||
LEASE=$(bun tools/device-pod-cli.ts lease acquire --pod-id D601-F103-V2 \
|
||
--api-base-url http://74.48.78.17:19667 \
|
||
--cookie "$COOKIE" \
|
||
--reason "D601-F103-V2 build/download validation" | node -e 'let s="";process.stdin.on("data",d=>s+=d).on("end",()=>console.log(JSON.parse(s).body.leaseToken))')
|
||
|
||
bun tools/device-pod-cli.ts D601-F103-V2:workspace:/ build start \
|
||
--api-base-url http://74.48.78.17:19667 \
|
||
--cookie "$COOKIE" \
|
||
--lease-token "$LEASE" \
|
||
--reason "D601-F103-V2 Keil build validation" \
|
||
--timeout-ms 120000
|
||
|
||
bun tools/device-pod-cli.ts D601-F103-V2:debug-probe download start \
|
||
--api-base-url http://74.48.78.17:19667 \
|
||
--cookie "$COOKIE" \
|
||
--lease-token "$LEASE" \
|
||
--reason "D601-F103-V2 Keil download validation" \
|
||
--capture-uart uart/1 \
|
||
--capture-duration-ms 8000 \
|
||
--timeout-ms 120000
|
||
```
|
||
|
||
Code-agent/DS runner 内执行同类验收时,把 `bun tools/device-pod-cli.ts` 换成 `hwpod`;只有 `hwpod` 不在 PATH 时才临时 fallback 到 `/app/skills/device-pod-cli/scripts/device-pod-cli.mjs`。UART 串口若被 Windows 侧工具占用,应把 COM busy/access denied 记录为可选串口证据缺口,Keil build/download 是否通过以 job output 中的 build summary、`Programming Done`、`Verify OK` 和 `Application running` 为准。
|
||
|
||
DS 或其他 code-agent runner 由 `hwlab-cli client harness submit` 触发时,控制面应保持短连接:提交后用 `client harness result`、`client harness trace --limit <N>` 和 `client harness audit --require-bootsharp` 轮询,不把 UniDesk `ssh/tran` 连接长期挂在一次 `wait` 上。`client harness wait` 只用于短窗口观察,CLI 会把超长等待压到低于 UniDesk 透传硬超时的安全窗口,并在 JSON 中返回后续短轮询命令。
|
||
|
||
job 观察优先使用 cloud-api job 入口,避免从 shell 管道里解析整段 JSON:
|
||
|
||
```bash
|
||
hwpod job status --pod-id D601-F103-V2 <jobId> --api-base-url <url> --cookie "$COOKIE"
|
||
hwpod job output --pod-id D601-F103-V2 <jobId> --api-base-url <url> --cookie "$COOKIE"
|
||
```
|
||
|
||
`build status/output/cancel <jobId>` 和 `download status/output/cancel <jobId>` 只作为 selector 便捷别名;当 `<jobId>` 是 `job_devicepod_*` 这类 cloud-api job 时,别名必须映射到 `/jobs/<jobId>` 或 `/jobs/<jobId>/output`,不得误启动新的 build/download job。Keil host 侧子 job 仍保留 `build status <hostJobId>` 或 `download status <hostJobId>` 语义,用于查看 Windows `device-host-cli` 创建的嵌套 job。
|
||
|
||
搜索下载、烧录或串口证据时,带空格、管道符或较长正则的 `workspace rg` 应使用显式参数,避免本地 shell、selector 和 Windows host 三层参数解析产生歧义:
|
||
|
||
```bash
|
||
hwpod D601-F103-V2:workspace:/ rg \
|
||
--pattern "Programming Done|Verify OK|Application running|Error" \
|
||
--path projects/01_baseline/captures \
|
||
--api-base-url <url> --cookie "$COOKIE"
|
||
```
|
||
|
||
验收通过条件:`client device-pods status` 返回 `status=ok` 且 `blocker=null`;gateway sessions 中 `gws_D601_F103` 为 online;`device-pod-cli` job 进入 `completed`,且 D601 Windows gateway `/status.lastDispatch.operationId` 与该 job 的 `operationId` 一致并有 `lastResultAt`。旧 `gws_d601_win_71_freq` session 只能作为历史 stale 线索,不能作为 v0.2 F103 gateway 的目标命名。
|
||
|
||
## 测试规格
|
||
|
||
## T1
|
||
|
||
阅读 docs/reference/spec-device-pod.md,然后用 cli 手动测试以下内容:普通用户无 grant 时访问 `/v1/device-pods` 不得看到任何 device pod;获得 grant 后能看到对应 devicePodId 和脱敏 profileHash,不能看到 gatewaySessionId、hostWorkspaceRoot 或 probe UID 等敏感路由字段。
|
||
|
||
## T2
|
||
|
||
阅读 docs/reference/spec-device-pod.md,然后用 cli 手动测试以下内容:尝试通过本地 `.device-pod/*.json` 修改 gateway route 或 workspace route,正式 cloud-api/device-pod 路径必须忽略该本地文件并继续使用服务端 profile authority。
|
||
|
||
## T3
|
||
|
||
阅读 docs/reference/spec-device-pod.md,然后用 cli 手动测试以下内容:提交一个强副作用 job,例如 download/reset,缺少 reason 或 lease 时必须被拒绝;获得 lease 后响应必须包含 devicePodId、profileHash、traceId、operationId、freshness、blocker 和 bounded output metadata。
|
||
|
||
## T4
|
||
|
||
阅读 docs/reference/spec-device-pod.md,然后用 cli 手动测试以下内容:对授权 Device Pod 运行 `workspace put`、`workspace rm`、`workspace rmdir`、`workspace keil add-source/remove-source` 和 `io-probe jsonrpc` 的 `--dry-run` 与一次真实小闭环。确认请求只经过 cloud-api job REST,输出 intent、reason、lease、traceId 和 bounded output,不读取本地 profile 或直连 gateway。
|
||
|
||
## 规格的实现情况
|
||
|
||
| 规格项 | 状态 | 说明 |
|
||
| --- | --- | --- |
|
||
| 逻辑 device-pod 模型 | 已实现为规格 | 四要素、profile shape 和 Kubernetes 口径已定义。 |
|
||
| profile server authority | 部分实现 | cloud-api 保存正式 DB profile 并向用户返回脱敏摘要;device-pod executor 不接受用户上传 profile。 |
|
||
| 用户 grant + lease | 部分实现 | cloud-api 已实现 admin grant、可见性过滤、lease acquire/current/release、强副作用 job lease 校验和撤销授权释放 lease。 |
|
||
| REST/job API | 部分实现 | cloud-api 已实现 list/status/events/probe/job/output/cancel 和 lease API,并可把已授权 job 转发给内部 `hwlab-device-pod` executor;executor 已实现内部 job create/get/output/cancel lifecycle 和 gateway/device-host-cli dispatch adapter,无在线 gateway/device-host-cli 时返回 blocker。 |
|
||
| G14 device-host 功能吸收 | 部分实现 | v0.2 job intent 已覆盖 workspace put/rm/rmdir、Keil 工程维护和 UART JSON-RPC,保持 cloud-api grant/lease/profile authority。 |
|
||
| 禁止 fake 作为 DEV-LIVE | 已实现/持续约束 | 规格和服务 payload 要求显式标记 fake/source。 |
|
||
|