23 KiB
name, description, version
| name | description | version |
|---|---|---|
| device-pod-cli | Use the HWLAB internal device-pod CLI from a HWLAB code agent runner to create/register device-pod profiles and operate a profiled target workspace, debug probe, and I/O probe through hwlab-gateway and device-host-cli. | 0.1.0-mvp |
Device Pod CLI
Skill(cli-spec)
Scope: this skill is only for HWLAB-internal code agent runners inside the
HWLAB runtime, such as /workspace/hwlab sessions running from the G14 DEV or
PROD images. It does not apply to external UniDesk developer workspaces or
third-party machines; those environments must not treat this skill as a general
hardware control contract.
Use this skill when the task mentions device-pod-cli, device-pod,
device-host-cli, HWLAB device-pod profile files, Keil build/download through a
device pod, debug probe control, or I/O probe reads such as UART boot logs.
Canonical skill location: /app/skills/device-pod-cli/SKILL.md. Do not look
for or create duplicate skill copies under /root/.agents/skills,
/home/ubuntu/.agents/skills, or /workspace/hwlab/skills; those locations are
not the HWLAB runner contract and cause stale instructions.
DeepSeek Operation Friction Rules
- For build, download, UART, and workspace actions, use
device-pod-clifirst. Do not start by calling/app/tools/hwlab-gateway-tran.mjsdirectly, do not hunt for Windows-side Keil skills, and do not callkeil-cli.pydirectly when a matching device-pod profile exists. - The profile already carries
hostWorkspaceRoot,projectWorkspace,debugInterface.uv4Path, probe UID, UART port, and gateway route. Letdevice-pod-cli -> device-host-cliapply those values; do not rediscover the Windows project path withdir,Get-ChildItem, or ad hoccmdprobes. - If a command returns
profile-missing, run profile bootstrap. If it returns a device-host-cli JSON failure, fix the named device-pod operation or host CLI; do not bypass the profile with generic gateway shell. - Workspace search follows a compact ripgrep-like shape:
device-pod-71-freq:workspace:/ rg "printf|main" projects/01_baseline -g "*.c" -g "*.h" -l. The optional path after the pattern is honored as the search root; use it to avoid scanning vendor PDFs or generated text outside the project under test. - For debug probe jobs,
download startis mutating and requires--approved --reason, butdownload status <jobId>is read-only and must be queried without approval friction. Ifdownload statusincludes UART capture, read the top-levelhostUartCapturefield first before falling back to job files or directtran. - I/O probe selectors are strict path tokens. Always write
device-pod-71-freq:io-probe:/uart/1 readas one selector token; do not writeio-probe:/uart / 1 read,io-probe:/uart/ 1 read, or other spaced path variants. The CLI should fail these forms with aninvalid-requesthint instead of normalizing the path or forwarding the malformed command to the Windows host.
Fast build smoke for device-pod-71-freq:
node /app/skills/device-pod-cli/scripts/device-pod-cli.mjs profile show --pod-id device-pod-71-freq
node /app/skills/device-pod-cli/scripts/device-pod-cli.mjs device-pod-71-freq:workspace:/ build start
node /app/skills/device-pod-cli/scripts/device-pod-cli.mjs device-pod-71-freq:workspace:/ build status <jobId>
The build status JSON must be treated as the evidence. Report
hostJson.status, hostJson.result.success, hostJson.result.exitCode,
hostJson.result.project, hostJson.result.target, and the output artifact
paths from the profile; do not describe Windows Keil skill config as the source
of truth for a device-pod build.
DeepSeek Profile-Recreate Task
When a HWLAB runner asks DeepSeek or another code agent to delete and recreate a profile, this SKILL.md is the operating procedure. Do not stop after listing the workspace or describing files. Execute the profile bootstrap and validation commands, then report the resulting JSON evidence.
For device-pod-71-00075-11, the required order is:
- Prove the profile is missing with
node /app/skills/device-pod-cli/scripts/device-pod-cli.mjs health --pod-id device-pod-71-00075-11. - Create
/workspace/hwlab/.device-pod/device-pod-71-00075-11.jsonwithnode /app/skills/device-pod-cli/scripts/device-pod-cli.mjs profile create --pod-id device-pod-71-00075-11 --force. - Upload
/app/skills/device-pod-cli/assets/device-host-cli.mjsand the new profile togws_DESKTOP-1MHOD9I:/f/Work/ConStart/projects/71-00075-11using/app/tools/tran.mjs. - Verify Windows host health through
node tools\device-host-cli.mjs --profile .device-pod\device-pod-71-00075-11.json --pod-id device-pod-71-00075-11 health. - Run
node /app/skills/device-pod-cli/scripts/device-pod-cli.mjs profile listand report the profilecountplus the hash of each profile. - Verify runner health, selector dry-runs, workspace
ls, build start/status, download start/status, and UART read/write/read throughdevice-pod-cli.
Any failure must be reported as FAIL with the exact command and the JSON
blocker or stderr/stdout summary. A successful bootstrap report must include
runner profile path, profile SHA-256, profile list count, host health or the
observed gateway blocker, runner health, and both selector dry-run results.
Full hardware validation reports must additionally include build job id,
download job id, download Verify OK evidence, and UART byte count/summary.
For the first bootstrap turn, run this block exactly. It intentionally performs only missing-state proof, runner profile creation, host asset/profile upload, host health, runner health, and selector dry-run checks; run build/download/UART in a second turn after this block succeeds.
Preferred one-line flow for DeepSeek runners: use the CLI-owned profile template instead of hand-copying JSON. This avoids heredoc/quoting drift in model-run shells and still creates the same explicit profile under the persistent profile directory.
node /app/skills/device-pod-cli/scripts/device-pod-cli.mjs profile create --pod-id device-pod-71-00075-11 --force
node /app/tools/tran.mjs gws_DESKTOP-1MHOD9I:/f/Work/ConStart/projects/71-00075-11 upload /app/skills/device-pod-cli/assets/device-host-cli.mjs tools/device-host-cli.mjs
node /app/tools/tran.mjs gws_DESKTOP-1MHOD9I:/f/Work/ConStart/projects/71-00075-11 upload /workspace/hwlab/.device-pod/device-pod-71-00075-11.json .device-pod/device-pod-71-00075-11.json
node /app/tools/tran.mjs gws_DESKTOP-1MHOD9I:/f/Work/ConStart/projects/71-00075-11 cmd -- node tools\device-host-cli.mjs --profile .device-pod\device-pod-71-00075-11.json --pod-id device-pod-71-00075-11 health
node /app/skills/device-pod-cli/scripts/device-pod-cli.mjs health --pod-id device-pod-71-00075-11
node /app/skills/device-pod-cli/scripts/device-pod-cli.mjs profile list
node /app/skills/device-pod-cli/scripts/device-pod-cli.mjs device-pod-71-00075-11:workspace:/ ls --dry-run
node /app/skills/device-pod-cli/scripts/device-pod-cli.mjs device-pod-71-freq:workspace:/ ls --dry-run
The lower-level JSON block below is a reference for the CLI template and for manual recovery only.
set -eu
skill_file=/app/skills/device-pod-cli/SKILL.md
grep -n "DeepSeek Profile-Recreate Task" "$skill_file"
set +e
node /app/skills/device-pod-cli/scripts/device-pod-cli.mjs health --pod-id device-pod-71-00075-11
missing_exit=$?
set -e
if [ "$missing_exit" -eq 0 ]; then
echo "FAIL: expected profile-missing before bootstrap" >&2
exit 10
fi
profile_dir="${DEVICE_POD_PROFILE_DIR:-/workspace/hwlab/.device-pod}"
profile_file="$profile_dir/device-pod-71-00075-11.json"
mkdir -p "$profile_dir"
cat > "$profile_file" <<'JSON'
{
"schemaVersion": 1,
"podId": "device-pod-71-00075-11",
"devicePodId": "device-pod-71-00075-11",
"workspaceRoot": "F:\\Work\\ConStart\\projects\\71-00075-11",
"target": {
"id": "constart-71-00075-11",
"mcu": "STM32H723ZGTx",
"flashBase": "0x08000000"
},
"projectWorkspace": {
"projectPath": "FirmWare/MDK-ARM/FREQ_Controller_FW.uvprojx",
"targetName": "FREQ_Controller_FW",
"hexPath": "FirmWare/MDK-ARM/FREQ_Controller_FW/FREQ_Controller_FW.hex",
"mapPath": "FirmWare/MDK-ARM/FREQ_Controller_FW/FREQ_Controller_FW.map"
},
"debugInterface": {
"id": "debug-probe",
"type": "cmsis-dap",
"probeUid": "3FD750C63E342E24",
"uv4Path": "C:\\Keil_v5\\UV4\\UV4.exe",
"programBackend": "keil-headless",
"autoBindUvoptx": true
},
"ioInterface": {
"uart": [
{
"id": "uart/1",
"scope": "external",
"port": "COM4",
"baudRate": 921600,
"encoding": "utf8",
"description": "ConStart debug USART1 via CMSIS-DAP CDC UART (verified COM4)"
}
]
},
"cloudApiUrl": "http://hwlab-cloud-api.hwlab-dev.svc.cluster.local:6667",
"gatewaySessionId": "gws_DESKTOP-1MHOD9I",
"resourceId": "res_windows_host",
"capabilityId": "cap_windows_cmd_exec",
"hostWorkspaceRoot": "F:\\Work\\ConStart\\projects\\71-00075-11",
"hostCli": "node tools\\device-host-cli.mjs"
}
JSON
sha256sum "$profile_file"
node /app/tools/tran.mjs gws_DESKTOP-1MHOD9I:/f/Work/ConStart/projects/71-00075-11 upload /app/skills/device-pod-cli/assets/device-host-cli.mjs tools/device-host-cli.mjs
node /app/tools/tran.mjs gws_DESKTOP-1MHOD9I:/f/Work/ConStart/projects/71-00075-11 upload "$profile_file" .device-pod/device-pod-71-00075-11.json
node /app/tools/tran.mjs gws_DESKTOP-1MHOD9I:/f/Work/ConStart/projects/71-00075-11 cmd -- node tools\device-host-cli.mjs --profile .device-pod\device-pod-71-00075-11.json --pod-id device-pod-71-00075-11 health
node /app/skills/device-pod-cli/scripts/device-pod-cli.mjs health --pod-id device-pod-71-00075-11
node /app/skills/device-pod-cli/scripts/device-pod-cli.mjs profile list
node /app/skills/device-pod-cli/scripts/device-pod-cli.mjs device-pod-71-00075-11:workspace:/ ls --dry-run
node /app/skills/device-pod-cli/scripts/device-pod-cli.mjs device-pod-71-freq:workspace:/ ls --dry-run
Runtime Contract
- The preinstalled CLI entrypoint is
node /app/skills/device-pod-cli/scripts/device-pod-cli.mjs. - The canonical implementation is
/app/tools/device-pod-cli.mjs; the skill script is only a stable preinstalled wrapper for code agent runners. - The packaged Windows host CLI asset is
/app/skills/device-pod-cli/assets/device-host-cli.mjs. It is bundled with this skill so a runner can send the exact tested host-side CLI to a new Windows hardware PC before the first device-pod operation. - Profiles are read from
DEVICE_POD_PROFILE_DIR/<devicePodId>.jsonwhen that directory is set, otherwise from.device-pod/<devicePodId>.jsonin the current code agent workspace;DEVICE_POD_PROFILEmay point to one explicit profile file. G14 device-agent runners should keep shared profiles in the persistent/workspace/hwlab/.device-poddirectory rather than only in ephemeral/app/.device-pod. - Multiple profiles may coexist in the same runner workspace. Use the selector
prefix, such as
device-pod-71-freq:...ordevice-pod-71-00075-11:..., as the source of truth for the active pod.device-pod-cliautomatically passes that selected profile to Windowsdevice-host-clias--profile .device-pod\\<devicePodId>.json --pod-id <devicePodId>, so host-side commands must not fall back to the defaultdevice-pod-71-freqprofile when another pod is selected. - The CLI calls HWLAB cloud-api
hardware.invoke.shell, which dispatches to an outboundhwlab-gatewaysession and then to the user PC sidedevice-host-cli. Do not call gateway, serial ports, Keil, pyOCD, DAPLink, or Windows shell directly when the profiled device-pod path is available. device-host-cliis expected to be self-contained on the user PC. Keil, serial-monitor, mklink, and other skills can be implementation references, but are not runtime dependencies of this HWLAB runner skill.device-host-cliis not just a copied preinstall script. It is the hot development tool that HWLAB internal code agents, including DeepSeek-backed runners, may keep improving on the target Windows workspace when hardware, Keil, probe, UART, or filesystem operations are not smooth enough. Hot fixes are expected whenever host-side behavior is awkward or missing; validate them through the real device-pod/gateway route first, then copy the result back into the HWLAB repo asset for the next CI/CD preinstall.
Profile Bootstrap
A missing profile is a normal bootstrap state. Do not switch to another profile and do not guess defaults. Create the selected device-pod profile explicitly in both places below, then verify it before running build, download, or UART I/O.
- Runner profile:
/workspace/hwlab/.device-pod/<devicePodId>.json. This is the persistent HWLAB code agent workspace profile used bydevice-pod-cli. - Windows host profile:
<hostWorkspaceRoot>\.device-pod\<devicePodId>.json. This is the matching profile loaded bydevice-host-cliafter the gateway command reaches the Windows hardware PC. - Windows host CLI:
<hostWorkspaceRoot>\tools\device-host-cli.mjs. Upload the packaged/app/skills/device-pod-cli/assets/device-host-cli.mjsasset when a new profile is bootstrapped or after the host CLI has been hot-fixed.
Minimum profile fields are schemaVersion, podId or devicePodId,
target.id, cloudApiUrl, gatewaySessionId, resourceId, capabilityId,
hostWorkspaceRoot, and hostCli. Keep the project, debug probe, and I/O probe
sections explicit so later agents do not depend on hidden CLI overrides.
For G14 DEV ConStart device-pod-71-00075-11, use this known hardware profile.
This example is intentionally complete so a DeepSeek runner can recreate the
profile after /workspace/hwlab/.device-pod/device-pod-71-00075-11.json and the
matching Windows profile have been deleted.
profile_dir="${DEVICE_POD_PROFILE_DIR:-/workspace/hwlab/.device-pod}"
profile_file="$profile_dir/device-pod-71-00075-11.json"
mkdir -p "$profile_dir"
cat > "$profile_file" <<'JSON'
{
"schemaVersion": 1,
"podId": "device-pod-71-00075-11",
"devicePodId": "device-pod-71-00075-11",
"workspaceRoot": "F:\\Work\\ConStart\\projects\\71-00075-11",
"target": {
"id": "constart-71-00075-11",
"mcu": "STM32H723ZGTx",
"flashBase": "0x08000000"
},
"projectWorkspace": {
"projectPath": "FirmWare/MDK-ARM/FREQ_Controller_FW.uvprojx",
"targetName": "FREQ_Controller_FW",
"hexPath": "FirmWare/MDK-ARM/FREQ_Controller_FW/FREQ_Controller_FW.hex",
"mapPath": "FirmWare/MDK-ARM/FREQ_Controller_FW/FREQ_Controller_FW.map"
},
"debugInterface": {
"id": "debug-probe",
"type": "cmsis-dap",
"probeUid": "3FD750C63E342E24",
"uv4Path": "C:\\Keil_v5\\UV4\\UV4.exe",
"programBackend": "keil-headless",
"autoBindUvoptx": true
},
"ioInterface": {
"uart": [
{
"id": "uart/1",
"scope": "external",
"port": "COM4",
"baudRate": 921600,
"encoding": "utf8",
"description": "ConStart debug USART1 via CMSIS-DAP CDC UART (verified COM4)"
}
]
},
"cloudApiUrl": "http://hwlab-cloud-api.hwlab-dev.svc.cluster.local:6667",
"gatewaySessionId": "gws_DESKTOP-1MHOD9I",
"resourceId": "res_windows_host",
"capabilityId": "cap_windows_cmd_exec",
"hostWorkspaceRoot": "F:\\Work\\ConStart\\projects\\71-00075-11",
"hostCli": "node tools\\device-host-cli.mjs"
}
JSON
node /app/skills/device-pod-cli/scripts/device-pod-cli.mjs health --pod-id device-pod-71-00075-11
node /app/tools/tran.mjs gws_DESKTOP-1MHOD9I:/f/Work/ConStart/projects/71-00075-11 upload /app/skills/device-pod-cli/assets/device-host-cli.mjs tools/device-host-cli.mjs
node /app/tools/tran.mjs gws_DESKTOP-1MHOD9I:/f/Work/ConStart/projects/71-00075-11 upload "$profile_file" .device-pod/device-pod-71-00075-11.json
node /app/tools/tran.mjs gws_DESKTOP-1MHOD9I:/f/Work/ConStart/projects/71-00075-11 cmd -- node tools\\device-host-cli.mjs --profile .device-pod\\device-pod-71-00075-11.json --pod-id device-pod-71-00075-11 health
After bootstrap, validate selector isolation before touching hardware:
node /app/skills/device-pod-cli/scripts/device-pod-cli.mjs device-pod-71-00075-11:workspace:/ ls --dry-run
node /app/skills/device-pod-cli/scripts/device-pod-cli.mjs device-pod-71-freq:workspace:/ ls --dry-run
The two dry-run outputs must reference different --profile .device-pod\<devicePodId>.json host arguments and different profile hashes.
Windows Host CLI Bootstrap
Do not run a separate installer. To bring up a new Windows hardware PC, send the packaged asset to the target workspace through the existing cmd passthrough path, then start it through the same cmd passthrough path.
Target layout:
<windows-workspace>\tools\device-host-cli.mjs
<windows-workspace>\.device-pod\<devicePodId>.json
When the UniDesk Windows maintenance route is available, the file transfer may
use the same remote channel that backs cmd passthrough. In HWLAB coder images,
/app/tools/tran.mjs is the stable contract entrypoint;
/app/tools/hwlab-gateway-tran.mjs is only the compatible explicit wrapper for
the same transport. If the compatible wrapper exists but /app/tools/tran.mjs
is missing, report a CI/CD preinstall gap instead of adding a device-pod
workaround. In HWLAB k8s runner pods, tran.mjs auto-selects the in-cluster
hwlab-cloud-api.<namespace>.svc.cluster.local:6667 API endpoint; outside k8s it
falls back to local 127.0.0.1:6667, and non-standard deployments may still pass
--api-base-url. tran.mjs upload uses bounded base64 chunks tuned for the
Windows cmd passthrough limit; do not raise the chunk size above the documented
default to hide slow transfers. If upload/download becomes awkward again, fix
tran.mjs or the gateway transport first.
node /app/tools/tran.mjs <gatewaySessionId>:/d/Work/HWLAB-workspace upload /app/skills/device-pod-cli/assets/device-host-cli.mjs tools/device-host-cli.mjs
node /app/tools/tran.mjs <gatewaySessionId>:/d/Work/HWLAB-workspace cmd -- node tools\\device-host-cli.mjs --profile .device-pod\\<devicePodId>.json --pod-id <devicePodId> health
When only HWLAB gateway hardware.invoke.shell is available, send the asset
bytes as bounded base64 chunks through cmd/PowerShell into
tools\device-host-cli.mjs, then verify with:
node tools\device-host-cli.mjs --profile .device-pod\<devicePodId>.json --pod-id <devicePodId> health
After that, device-pod-cli should use the profile's hostCli, normally
node tools\device-host-cli.mjs, and should not depend on any Windows skill
directory at runtime.
Host CLI Hot Development
When a profiled operation is awkward or missing, prefer improving the Windows
workspace copy of tools\device-host-cli.mjs instead of bypassing the device-pod
model with arbitrary cmd snippets. The intended loop is:
edit tools\device-host-cli.mjs in the Windows device-pod workspace
node tools\device-host-cli.mjs health
node tools\device-host-cli.mjs <new named operation>
node /app/skills/device-pod-cli/scripts/device-pod-cli.mjs <devicePodId>:... <same operation>
copy the validated change back to skills/device-pod-cli/assets/device-host-cli.mjs
The host CLI should stay self-contained and named-operation oriented: add
debug-probe launch-flash or io-probe uart/1 read-after-launch-flash style
device semantics, not a generic shell escape. If gateway/cmd transfer itself is
the friction, fix tran.mjs or the gateway transport separately instead of
embedding transport workarounds into device-host-cli.
Commands
All commands emit JSON and must remain short-running. Use build start /
build status and download start / download status style host jobs for
long operations instead of holding one gateway request open.
node /app/skills/device-pod-cli/scripts/device-pod-cli.mjs health
node /app/skills/device-pod-cli/scripts/device-pod-cli.mjs device-pod-71-freq:workspace:/ ls
node /app/skills/device-pod-cli/scripts/device-pod-cli.mjs device-pod-71-freq:workspace:/ rg main
node /app/skills/device-pod-cli/scripts/device-pod-cli.mjs device-pod-71-freq:workspace:/ apply-patch --approved --reason "DEV edit" < patch.diff
node /app/skills/device-pod-cli/scripts/device-pod-cli.mjs device-pod-71-freq:workspace:/ build start
node /app/skills/device-pod-cli/scripts/device-pod-cli.mjs device-pod-71-freq:workspace:/ build status <jobId>
node /app/skills/device-pod-cli/scripts/device-pod-cli.mjs device-pod-71-freq:debug-probe chip-id
node /app/skills/device-pod-cli/scripts/device-pod-cli.mjs device-pod-71-freq:debug-probe download start --approved --reason "DEV smoke"
node /app/skills/device-pod-cli/scripts/device-pod-cli.mjs device-pod-71-freq:debug-probe download status <jobId>
node /app/skills/device-pod-cli/scripts/device-pod-cli.mjs device-pod-71-freq:debug-probe download start --capture-uart uart/1 --capture-duration-ms 8000 --approved --reason "DEV boot log capture"
node /app/skills/device-pod-cli/scripts/device-pod-cli.mjs device-pod-71-freq:debug-probe launch-flash --approved --reason "manual flash-vector launch"
node /app/skills/device-pod-cli/scripts/device-pod-cli.mjs device-pod-71-freq:io-probe:/uart/1 ports
node /app/skills/device-pod-cli/scripts/device-pod-cli.mjs device-pod-71-freq:io-probe:/uart/1 read --duration-ms 5000
node /app/skills/device-pod-cli/scripts/device-pod-cli.mjs device-pod-71-freq:io-probe:/uart/1 read --port COM4 --baud-rate 921600 --duration-ms 5000
node /app/skills/device-pod-cli/scripts/device-pod-cli.mjs device-pod-71-freq:io-probe:/uart/1 read-after-launch-flash --approved --reason "boot log capture"
Use --port COMx and --baud-rate N only as a short-lived UART discovery
override. After the real port is proven, write the stable value back into the
device-pod profile so later agents do not depend on a hidden command override.
For boot logs that only appear during reset/download, prefer
debug-probe download start --capture-uart uart/1 --capture-duration-ms N so
the host opens the UART before Keil starts flashing and stores the captured
bytes in the download job status under result.uartCapture.
Safety
- Mutating operations such as workspace patching, download, reset,
launch-flash,read-after-launch-flash, and I/O write require--approved --reason <text>before the CLI dispatches anything. io-probe:/inner/...is intentionally distinct from external physical I/O paths such asio-probe:/uart/1; do not use internal target state as evidence for external UART/GPIO observations.- Keep the gateway as transport only. Do not add a generic
cmdoperation todevice-pod-cli; add a named device-pod operation instead. - If the CLI returns
profile-missing,profile-invalid, orapproval-required, fix that blocker first rather than guessing a default device or falling back to arbitrary shell commands.