fix: align hwpod lease guidance
This commit is contained in:
@@ -24,6 +24,7 @@
|
||||
- `commitId` 必须是完整 40 字符小写 SHA,不接受 branch、tag、`HEAD` 或短 SHA。
|
||||
- `workspaceRef` 只描述目标 repo/branch,默认 branch 是 `v0.2`;实际 checkout 身份以 `resourceBundleRef.repoUrl + commitId` 为准。
|
||||
- 默认 `toolAliases` 会把 `tools/device-pod-cli.mjs` 暴露为 `hwpod`,并把 `tools/unidesk-ssh.mjs` 暴露为 runner shell 内的 `unidesk-ssh` 命令。Device Pod、D601-F103-V2、Keil build/download、job status/output 和 UART/debug-probe 操作必须优先走 `hwpod`;`unidesk-ssh` 只用于 UniDesk passthrough 任务,不能替代 Device Pod 正式路径。
|
||||
- Device Pod lease 不是独立 runner 控制命令;标准 `hwpod` surface 只提交命名 Device Pod job。需要 lease 时,token 只能作为 `--lease-token` 跟随实际 mutating job 请求转发给 cloud-api。Agent 不得调用 `hwpod lease ...`,也不得新增 gateway shell 或其他 fallback 来绕过 job authority。
|
||||
- 默认 `promptRefs` 指向 `internal/agent/prompts/hwlab-v02-runtime.md`,`inject=thread-start` 且 `required=true`。该 prompt 只在 AgentRun/Codex stdio 新 thread 首轮注入;后续 turn 必须依赖原生 `thread/resume`,不得在 command payload 中拼接历史、旧 skill 列表或长业务 prompt。
|
||||
- 默认 `skillRefs` 指向 `skills/device-pod-cli/SKILL.md` 和 `skills/hwlab-agent-runtime/SKILL.md`,都为 `required=true`,由 AgentRun 聚合到当前 workspace `.agents/skills/<name>/SKILL.md`。HWLAB 不再依赖 `/app/skills`、hostPath、默认 Codex skill registry、ConfigMap 或用户长 prompt 作为 skill 注入 fallback。
|
||||
- AgentRun runtime image 已预装 `gh`,结合 `tool=github` 注入的 `GH_TOKEN` 即可访问 HWLAB/UniDesk PR 与 issue;不得把 GitHub token 放入 prompt 或 `transientEnv`。
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: device-pod-cli
|
||||
description: Use the HWLAB v0.2 internal device-pod CLI from a HWLAB code agent runner to operate authorized Device Pods through cloud-api REST, leases, hwlab-device-pod, gateway, and device-host-cli.
|
||||
description: Use the HWLAB v0.2 internal device-pod CLI from a HWLAB code agent runner to operate authorized Device Pods through cloud-api REST jobs, hwlab-device-pod, gateway, and device-host-cli.
|
||||
version: 0.2.0-rest
|
||||
---
|
||||
|
||||
@@ -20,7 +20,7 @@ Canonical AgentRun skill location: `.agents/skills/device-pod-cli/SKILL.md` insi
|
||||
- The canonical implementation is `tools/device-pod-cli.mjs` from the same ResourceBundleRef commit, exposed through the `hwpod` tool alias.
|
||||
- All commands emit JSON. A command with no output is a failure.
|
||||
- Formal v0.2 mode is cloud-api authority: the CLI calls `/v1/device-pods...` REST APIs and must not read `.device-pod/*.json` as the source of truth for gateway session, workspace root, probe UID, UART port, or host CLI.
|
||||
- Server-side profile, grant, lease, job authority, and profile hash are owned by `hwlab-cloud-api`; `hwlab-device-pod` and `device-host-cli` execute only after cloud-api authorization.
|
||||
- Server-side profile, grant, job authority, profile hash, and any lease enforcement are owned by `hwlab-cloud-api`; `hwlab-device-pod` and `device-host-cli` execute only after cloud-api authorization.
|
||||
|
||||
## Configuration
|
||||
|
||||
@@ -62,19 +62,13 @@ hwpod <devicePodId>:workspace:/ bootsharp
|
||||
|
||||
For the D601 F103 v2 board, the registered `devicePodId` is `D601-F103-V2`; its server-side profile points to `F:\Work\D601-HWLAB`, Keil project `projects/01_baseline/Projects/MDK-ARM/atk_f103.uvprojx`, target `USART`, `C:\Keil_v5\UV4\UV4.exe`, and UART `COM9` at `115200`.
|
||||
|
||||
Diagnose auth, grant, lease, and gateway blockers without touching local profiles:
|
||||
Diagnose auth, grant, job-authority, and gateway blockers without touching local profiles:
|
||||
|
||||
```sh
|
||||
hwpod doctor --pod-id device-pod-71-freq
|
||||
```
|
||||
|
||||
Acquire and inspect a lease for mutating jobs:
|
||||
|
||||
```sh
|
||||
hwpod lease acquire --pod-id device-pod-71-freq --reason "build smoke"
|
||||
hwpod lease current --pod-id device-pod-71-freq
|
||||
hwpod lease release --pod-id device-pod-71-freq --lease-token <lease-token>
|
||||
```
|
||||
There is no standalone `hwpod lease acquire/current/release` command in the current v0.2 standard path. Do not preflight a Device Pod task by calling `hwpod lease ...`. When cloud-api requires a lease token, pass the provided token on the actual mutating job; otherwise omit `--lease-token`.
|
||||
|
||||
Run read-only workspace, debug, and I/O jobs:
|
||||
|
||||
@@ -157,6 +151,7 @@ Do not treat local files such as `.device-pod/device-pod-71-freq.json` or `DEVIC
|
||||
- If apply-patch fails, read the returned `patchHint`, re-read the current file with `workspace cat` or `workspace rg`, and retry a smaller exact-context hunk before using whole-file write.
|
||||
- Do not start by calling `/app/tools/hwlab-gateway-tran.mjs`, Windows-side Keil skills, serial-monitor skills, `keil-cli.py`, or generic shell when the Device Pod REST path is available.
|
||||
- If the CLI returns an auth, grant, lease, executor, or gateway blocker, fix that named blocker in the cloud-api/device-pod path; do not bypass the profile with generic gateway shell.
|
||||
- If the CLI returns `standalone_lease_command_removed`, use the actual Device Pod job command with `--reason` and optional `--lease-token`; do not call `hwpod lease acquire/current/release` or switch to a fallback transport.
|
||||
- The server-side profile already carries `hostWorkspaceRoot`, `projectWorkspace`, `debugInterface.uv4Path`, probe UID, UART port, and gateway route. Let `cloud-api -> hwlab-device-pod -> device-host-cli` apply those values.
|
||||
- Keep I/O selectors as strict path tokens, for example `device-pod-71-freq:io-probe:/uart/1 read`; do not split `/uart/1` across argv.
|
||||
- Use `--dry-run` to inspect the REST job request without dispatching it.
|
||||
@@ -169,7 +164,7 @@ Do not upload or recreate runner-side `.device-pod/*.json` as the formal profile
|
||||
|
||||
## Safety
|
||||
|
||||
- Mutating operations require a human/business reason and may require an active lease. Cloud-api is the enforcement point; the CLI only forwards `reason` and `leaseToken`.
|
||||
- Mutating operations require a human/business reason and may require a lease token supplied by the cloud-api/business flow. Cloud-api is the enforcement point; the CLI only forwards `reason` and `leaseToken` on the named job request.
|
||||
- `hwlab-gateway` is transport only. Do not add a generic `cmd` operation to `device-pod-cli`; add a named Device Pod operation instead.
|
||||
- `SOURCE`, `LOCAL`, `DRY-RUN`, fixture data, and local profile files cannot be reported as `DEV-LIVE` hardware evidence.
|
||||
|
||||
|
||||
@@ -322,6 +322,22 @@ test("device-pod-cli sends reason without extra token for mutating jobs", async
|
||||
assert.deepEqual(seen[0].body, { intent: "debug.reset", args: {}, reason: "reset smoke" });
|
||||
});
|
||||
|
||||
test("device-pod-cli rejects standalone lease command with standard path guidance", async () => {
|
||||
let called = false;
|
||||
const result = await runAssembledDevicePodCli(["lease", "current", "--pod-id", "D601-F103-V2"], {
|
||||
fetchImpl: async () => {
|
||||
called = true;
|
||||
return jsonResponse(500, { ok: false });
|
||||
},
|
||||
now: () => "2026-06-02T00:00:00.000Z"
|
||||
});
|
||||
assert.equal(result.exitCode, 1);
|
||||
assert.equal(called, false);
|
||||
assert.equal(result.payload.error.code, "standalone_lease_command_removed");
|
||||
assert.match(result.payload.error.message, /Device Pod jobs through cloud-api/);
|
||||
assert.equal(JSON.stringify(result.payload.error.details.next).includes("hwpod lease acquire/current/release"), true);
|
||||
});
|
||||
|
||||
test("device-pod-cli preserves common v0.1 hardware options in REST job args", async () => {
|
||||
const seen: any[] = [];
|
||||
const result = await runAssembledDevicePodCli([
|
||||
|
||||
@@ -45,6 +45,7 @@ async function dispatch(target: string, context: any) {
|
||||
if (target === "health") return health(context);
|
||||
if (target === "bootsharp") return bootsharpCommand(context);
|
||||
if (target === "profile") return profileCommand(context);
|
||||
if (target === "lease" || target === "leases") return removedStandaloneLeaseCommand();
|
||||
if (target === "job" || target === "jobs") return jobCommand(context);
|
||||
const selector = parseSelector(target);
|
||||
if (!selector) throw cliError("invalid_target_selector", `invalid target selector: ${target}`);
|
||||
@@ -169,6 +170,20 @@ async function profileCommand({ parsed, rest, env, fetchImpl }: any) {
|
||||
throw cliError("unsupported_profile_command", `unsupported profile command: ${subcommand}`);
|
||||
}
|
||||
|
||||
function removedStandaloneLeaseCommand() {
|
||||
throw cliError(
|
||||
"standalone_lease_command_removed",
|
||||
"standalone hwpod lease commands are not part of the v0.2 standard path; send named Device Pod jobs through cloud-api with --reason and, only when cloud-api requires it, --lease-token",
|
||||
{
|
||||
next: [
|
||||
"Run hwpod bootsharp --pod-id <devicePodId> before operating a pod.",
|
||||
"Run the actual Device Pod job, for example hwpod <devicePodId>:workspace:/ build start --reason TEXT.",
|
||||
"Do not call hwpod lease acquire/current/release or switch to a generic gateway shell."
|
||||
]
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
async function jobCommand({ parsed, rest, env, fetchImpl }: any) {
|
||||
const subcommand = rest[0] || "status";
|
||||
const podId = requiredText(parsed.podId, "podId");
|
||||
|
||||
Reference in New Issue
Block a user