From ca714bfa2764afb558591ab4adec898c3f4a17a6 Mon Sep 17 00:00:00 2001 From: Codex Date: Sun, 31 May 2026 19:16:13 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BC=98=E5=8C=96=20device-pod=20CLI=20?= =?UTF-8?q?=E7=9F=AD=E5=85=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../launcher/hwlab-env-reuse-launcher.ts | 14 +++- docs/reference/spec-device-pod.md | 5 +- internal/cloud/codex-stdio-session.ts | 2 +- scripts/artifact-publish.mjs | 3 +- scripts/g14-gitops-render.test.ts | 1 + skills/device-pod-cli/SKILL.md | 66 +++++++++---------- tools/hwlab-cli/client.test.ts | 27 ++++++++ tools/src/device-pod-cli-lib.ts | 34 +++++----- tools/src/hwlab-cli-lib.ts | 4 +- 9 files changed, 100 insertions(+), 56 deletions(-) diff --git a/deploy/runtime/launcher/hwlab-env-reuse-launcher.ts b/deploy/runtime/launcher/hwlab-env-reuse-launcher.ts index ca7da95a..2acb8bcf 100644 --- a/deploy/runtime/launcher/hwlab-env-reuse-launcher.ts +++ b/deploy/runtime/launcher/hwlab-env-reuse-launcher.ts @@ -1,5 +1,5 @@ import { spawnSync } from "node:child_process"; -import { existsSync, mkdirSync, rmSync, symlinkSync } from "node:fs"; +import { chmodSync, existsSync, mkdirSync, rmSync, symlinkSync, writeFileSync } from "node:fs"; import path from "node:path"; const bootRepo = requiredEnv("HWLAB_BOOT_REPO"); @@ -24,6 +24,7 @@ rmSync(checkoutDir, { recursive: true, force: true }); run("git", ["clone", "--no-checkout", "--single-branch", "--branch", bootRef, readUrl, checkoutDir], "/"); run("git", ["checkout", "--detach", bootCommit], checkoutDir); linkNodeModules(); +installDevicePodAlias(); const scriptPath = path.join(checkoutDir, bootSh); if (!existsSync(scriptPath)) fail(`boot script not found: ${bootSh}`); @@ -48,6 +49,13 @@ function linkNodeModules(): void { symlinkSync(runtimeNodeModules, target, "dir"); } +function installDevicePodAlias(): void { + const wrapper = "/usr/local/bin/hwpod"; + const cli = path.join(checkoutDir, "tools", "device-pod-cli.mjs"); + writeFileSync(wrapper, `#!/usr/bin/env sh\nexec node ${shellArg(cli)} "$@"\n`, { encoding: "utf8" }); + chmodSync(wrapper, 0o755); +} + function run(command: string, args: string[], cwd: string, options: { replace?: boolean } = {}): void { const result = spawnSync(command, args, { cwd, @@ -65,3 +73,7 @@ function fail(message: string): never { process.stderr.write(`${JSON.stringify({ status: "failed", error: "hwlab_env_reuse_launcher_failed", message })}\n`); process.exit(1); } + +function shellArg(value: string): string { + return `'${value.replace(/'/gu, `'"'"'`)}'`; +} diff --git a/docs/reference/spec-device-pod.md b/docs/reference/spec-device-pod.md index d09fd357..135189b8 100644 --- a/docs/reference/spec-device-pod.md +++ b/docs/reference/spec-device-pod.md @@ -259,7 +259,7 @@ manages: many devicePodId ## CLI 实现口径 -`tools/device-pod-cli.ts` 是 v0.2 正式 CLI 实现,稳定 runner 入口仍是 `/app/skills/device-pod-cli/scripts/device-pod-cli.mjs`。该 `.mjs` 只作为兼容启动器,通过 Bun 运行 TypeScript CLI。正式 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。 @@ -271,6 +271,7 @@ manages: many devicePodId - 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 @@ -383,6 +384,8 @@ bun tools/device-pod-cli.ts D601-F103-V2:debug-probe download start \ --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` 为准。 + 验收通过条件:`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 的目标命名。 ## 测试规格 diff --git a/internal/cloud/codex-stdio-session.ts b/internal/cloud/codex-stdio-session.ts index 4acd379b..da6a0312 100644 --- a/internal/cloud/codex-stdio-session.ts +++ b/internal/cloud/codex-stdio-session.ts @@ -204,7 +204,7 @@ export const CODEX_STDIO_BOUNDARY_INSTRUCTIONS = [ "Use the provided workspace and repo-owned Codex stdio session only.", "Do not read or print secrets, tokens, kubeconfig files, DB URLs, private keys, or raw environment values.", "For hardware, gateway, box-simu, patch-panel, DAP, PWM, Keil, serial, and Windows skill requests, execute the requested work through the repo-owned Codex stdio session with the available tran wrapper, skill CLI, or project tool that actually reaches the target.", - "For any request that mentions device-pod, device-pod-cli, device-host-cli, a device-pod profile, device-pod-71-freq, device-pod-71-00075-11, D601-F103-V2, or a registered device-pod Keil build/download/UART/debug-probe operation, first use the HWLAB internal skill named device-pod-cli. The canonical skill location is /app/skills/device-pod-cli/SKILL.md; read that manifest and run node /app/skills/device-pod-cli/scripts/device-pod-cli.mjs for the operation. After selecting the target pod or resuming context, run bootsharp --pod-id before edits, build, or download.", + "For any request that mentions device-pod, device-pod-cli, device-host-cli, a device-pod profile, device-pod-71-freq, device-pod-71-00075-11, D601-F103-V2, or a registered device-pod Keil build/download/UART/debug-probe operation, first use the HWLAB internal skill named device-pod-cli. The canonical skill location is /app/skills/device-pod-cli/SKILL.md; read that manifest and run hwpod for the operation. Use node /app/skills/device-pod-cli/scripts/device-pod-cli.mjs only as a compatibility fallback when hwpod is absent. After selecting the target pod or resuming context, run bootsharp --pod-id before edits, build, or download.", "When a matching device-pod profile exists, do not bypass device-pod-cli with direct hwlab-gateway-tran.mjs, Windows Keil skills, serial-monitor skills, keil-cli.py, or ad hoc path discovery. Only drop to tran or Windows-side skills when the device-pod-cli skill explicitly says to bootstrap, install, or repair the lower layer.", "For registered PC gateway Windows command or skill requests, use the preloaded tran wrapper: node /app/tools/hwlab-gateway-tran.mjs gws_DESKTOP-1MHOD9I:/f/work [options] -- . The locator before ':' is the gateway session and the path after ':' is the Windows workspace, with /f/work and f:/work both mapping to F:\\work.", "For Windows cmd, call tran as: node /app/tools/hwlab-gateway-tran.mjs gws_DESKTOP-1MHOD9I:/f/work cmd -- . For PowerShell, call tran as: node /app/tools/hwlab-gateway-tran.mjs gws_DESKTOP-1MHOD9I:/f/work ps --