feat: assemble AgentRun prompts and skills
This commit is contained in:
@@ -23,11 +23,14 @@
|
||||
- AgentRun run 必须使用 Git-only `resourceBundleRef`,默认 repo 是 `http://git-mirror-http.devops-infra.svc.cluster.local/pikasTech/HWLAB.git`。
|
||||
- `commitId` 必须是完整 40 字符小写 SHA,不接受 branch、tag、`HEAD` 或短 SHA。
|
||||
- `workspaceRef` 只描述目标 repo/branch,默认 branch 是 `v0.2`;实际 checkout 身份以 `resourceBundleRef.repoUrl + commitId` 为准。
|
||||
- 默认 `toolAliases` 会把 `tools/unidesk-ssh.mjs` 暴露为 runner shell 内的 `unidesk-ssh` 命令;它只实现 AgentRun Code Agent 所需的短连接 UniDesk SSH passthrough,例如 `unidesk-ssh G14:/root/hwlab-v02 script -- 'pwd'`,完整文件传输和 patch 仍走 UniDesk CLI 原入口。
|
||||
- 默认 `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 正式路径。
|
||||
- 默认 `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`。
|
||||
|
||||
## 验收
|
||||
|
||||
- 源码合同测试:`node --test internal/agent/agentrun-dispatch.test.mjs`。
|
||||
- 语法检查:`node --check internal/agent/agentrun-dispatch.mjs && node --check internal/agent/agentrun-dispatch.test.mjs`。
|
||||
- 合同必须证明 `UNIDESK_SSH_CLIENT_TOKEN` 不出现在 `transientEnv`,GitHub/UniDesk SSH 能力都通过 AgentRun `toolCredentials` SecretRef 装配,且 runner resource bundle 默认暴露 `unidesk-ssh` 可执行别名。
|
||||
- 合同必须证明 `UNIDESK_SSH_CLIENT_TOKEN` 不出现在 `transientEnv`,GitHub/UniDesk SSH 能力都通过 AgentRun `toolCredentials` SecretRef 装配,且 runner resource bundle 默认暴露 `hwpod`、`unidesk-ssh`、`promptRefs` 和 `skillRefs`。
|
||||
- 真实 CLI 验收必须使用短 prompt 走 `backendProfile=deepseek`:首轮 prompt “不调用工具的情况下,你可见的 skill 有哪些?”应能回答 HWLAB bundle skill;同一会话 continuation 应显示 AgentRun 原生 resume 语义且不重复注入 initial prompt;“编译 D601-F103-V2”应触发 `hwpod` Device Pod 路径,若失败则报告正式 blocker,不切换 fallback。
|
||||
|
||||
@@ -7,8 +7,16 @@ export const DEFAULT_HWLAB_AGENTRUN_BRANCH = "v0.2";
|
||||
export const DEFAULT_HWLAB_AGENTRUN_BACKEND_PROFILE = "deepseek";
|
||||
export const DEFAULT_UNIDESK_MAIN_SERVER_ENV = "UNIDESK_MAIN_SERVER_IP";
|
||||
export const DEFAULT_HWLAB_AGENTRUN_TOOL_ALIASES = Object.freeze([
|
||||
{ name: "hwpod", path: "tools/device-pod-cli.mjs", kind: "node-script" },
|
||||
{ name: "unidesk-ssh", path: "tools/unidesk-ssh.mjs", kind: "bun-script" }
|
||||
]);
|
||||
export const DEFAULT_HWLAB_AGENTRUN_PROMPT_REFS = Object.freeze([
|
||||
{ name: "hwlab-v02-runtime", path: "internal/agent/prompts/hwlab-v02-runtime.md", inject: "thread-start", required: true }
|
||||
]);
|
||||
export const DEFAULT_HWLAB_AGENTRUN_SKILL_REFS = Object.freeze([
|
||||
{ name: "device-pod-cli", path: "skills/device-pod-cli/SKILL.md", required: true, aggregateAs: "device-pod-cli" },
|
||||
{ name: "hwlab-agent-runtime", path: "skills/hwlab-agent-runtime/SKILL.md", required: true, aggregateAs: "hwlab-agent-runtime" }
|
||||
]);
|
||||
|
||||
export const AGENTRUN_REUSABLE_CREDENTIAL_ENV_NAMES = Object.freeze([
|
||||
"AUTH_PASSWORD",
|
||||
@@ -86,6 +94,8 @@ export function createHwlabAgentRunDispatchAssembly(options = {}) {
|
||||
const timeoutMs = positiveInteger(options.timeoutMs ?? 600000, "timeoutMs");
|
||||
const unideskMainServerIp = optionalString(options.unideskMainServerIp ?? env.UNIDESK_MAIN_SERVER_IP ?? env.UNIDESK_MAIN_SERVER_HOST);
|
||||
const toolAliases = normalizeResourceToolAliases(options.toolAliases ?? DEFAULT_HWLAB_AGENTRUN_TOOL_ALIASES);
|
||||
const promptRefs = normalizeResourcePromptRefs(options.promptRefs ?? DEFAULT_HWLAB_AGENTRUN_PROMPT_REFS);
|
||||
const skillRefs = normalizeResourceSkillRefs(options.skillRefs ?? DEFAULT_HWLAB_AGENTRUN_SKILL_REFS);
|
||||
if (includeUnideskSshToolCredential && !unideskMainServerIp) {
|
||||
throw new Error("unideskMainServerIp is required when UniDesk SSH passthrough is enabled");
|
||||
}
|
||||
@@ -104,6 +114,8 @@ export function createHwlabAgentRunDispatchAssembly(options = {}) {
|
||||
...(options.subdir ? { subdir: nonEmptyString(options.subdir, "subdir") } : {}),
|
||||
...(Array.isArray(options.sparsePaths) ? { sparsePaths: options.sparsePaths.map((item, index) => nonEmptyString(item, `sparsePaths[${index}]`)) } : {}),
|
||||
...(toolAliases.length > 0 ? { toolAliases } : {}),
|
||||
...(promptRefs.length > 0 ? { promptRefs } : {}),
|
||||
...(skillRefs.length > 0 ? { skillRefs } : {}),
|
||||
...(options.resourceCredentialRef ? { credentialRef: options.resourceCredentialRef } : {})
|
||||
};
|
||||
|
||||
@@ -165,6 +177,8 @@ export function createHwlabAgentRunDispatchAssembly(options = {}) {
|
||||
unideskSshCredentialSource: includeUnideskSshToolCredential ? "toolCredentials" : null,
|
||||
unideskMainServerSource: unideskMainServerIp ? "transientEnv" : null,
|
||||
toolAliases: toolAliases.map((item) => item.name),
|
||||
promptRefs: promptRefs.map((item) => item.name),
|
||||
skillRefs: skillRefs.map((item) => item.name),
|
||||
transientEnvNames: transientEnv.map((item) => item.name),
|
||||
forbiddenTransientEnvNames: AGENTRUN_REUSABLE_CREDENTIAL_ENV_NAMES.slice()
|
||||
}
|
||||
@@ -259,6 +273,48 @@ function normalizeResourceToolAliases(items) {
|
||||
});
|
||||
}
|
||||
|
||||
function normalizeResourcePromptRefs(items) {
|
||||
if (!Array.isArray(items)) throw new Error("promptRefs must be an array");
|
||||
const seen = new Set();
|
||||
return items.map((item, index) => {
|
||||
if (!item || typeof item !== "object") throw new Error(`promptRefs[${index}] must be an object`);
|
||||
const name = resourceName(item.name, `promptRefs[${index}].name`);
|
||||
if (seen.has(name)) throw new Error(`promptRefs name ${name} is duplicated`);
|
||||
seen.add(name);
|
||||
const refPath = resourcePath(item.path, `promptRefs[${index}].path`);
|
||||
const inject = optionalString(item.inject) ?? "thread-start";
|
||||
if (inject !== "thread-start") throw new Error(`promptRefs[${index}].inject must be thread-start`);
|
||||
return { name, path: refPath, inject: "thread-start", required: item.required !== false };
|
||||
});
|
||||
}
|
||||
|
||||
function normalizeResourceSkillRefs(items) {
|
||||
if (!Array.isArray(items)) throw new Error("skillRefs must be an array");
|
||||
const seen = new Set();
|
||||
return items.map((item, index) => {
|
||||
if (!item || typeof item !== "object") throw new Error(`skillRefs[${index}] must be an object`);
|
||||
const name = resourceName(item.name, `skillRefs[${index}].name`);
|
||||
if (seen.has(name)) throw new Error(`skillRefs name ${name} is duplicated`);
|
||||
seen.add(name);
|
||||
const refPath = resourcePath(item.path, `skillRefs[${index}].path`);
|
||||
if (!refPath.endsWith("SKILL.md")) throw new Error(`skillRefs[${index}].path must point to SKILL.md`);
|
||||
const aggregateAs = optionalString(item.aggregateAs);
|
||||
return { name, path: refPath, required: item.required !== false, ...(aggregateAs ? { aggregateAs: resourceName(aggregateAs, `skillRefs[${index}].aggregateAs`) } : {}) };
|
||||
});
|
||||
}
|
||||
|
||||
function resourceName(value, fieldName) {
|
||||
const name = nonEmptyString(value, fieldName);
|
||||
if (!/^[a-z][a-z0-9._-]{0,62}$/u.test(name)) throw new Error(`${fieldName} must be a lowercase resource name`);
|
||||
return name;
|
||||
}
|
||||
|
||||
function resourcePath(value, fieldName) {
|
||||
const refPath = nonEmptyString(value, fieldName);
|
||||
if (refPath.startsWith("/") || refPath.includes("..")) throw new Error(`${fieldName} must stay within the checkout`);
|
||||
return refPath;
|
||||
}
|
||||
|
||||
async function postJson(fetchImpl, managerUrl, path, payload) {
|
||||
const response = await fetchImpl(`${managerUrl}${path}`, {
|
||||
method: "POST",
|
||||
|
||||
@@ -26,8 +26,18 @@ test("HWLAB AgentRun assembly grants GitHub and UniDesk SSH through toolCredenti
|
||||
assert.equal(assembly.runPayload.backendProfile, "deepseek");
|
||||
assert.equal(assembly.runPayload.workspaceRef.branch, "v0.2");
|
||||
assert.equal(assembly.runPayload.resourceBundleRef.commitId, commitId);
|
||||
assert.deepEqual(assembly.runPayload.resourceBundleRef.toolAliases.map((item) => item.name), ["unidesk-ssh"]);
|
||||
assert.equal(assembly.runPayload.resourceBundleRef.toolAliases[0].path, "tools/unidesk-ssh.mjs");
|
||||
assert.deepEqual(assembly.runPayload.resourceBundleRef.toolAliases.map((item) => item.name), ["hwpod", "unidesk-ssh"]);
|
||||
assert.equal(assembly.runPayload.resourceBundleRef.toolAliases[0].path, "tools/device-pod-cli.mjs");
|
||||
assert.deepEqual(assembly.runPayload.resourceBundleRef.promptRefs, [
|
||||
{ name: "hwlab-v02-runtime", path: "internal/agent/prompts/hwlab-v02-runtime.md", inject: "thread-start", required: true }
|
||||
]);
|
||||
assert.deepEqual(assembly.runPayload.resourceBundleRef.skillRefs, [
|
||||
{ name: "device-pod-cli", path: "skills/device-pod-cli/SKILL.md", required: true, aggregateAs: "device-pod-cli" },
|
||||
{ name: "hwlab-agent-runtime", path: "skills/hwlab-agent-runtime/SKILL.md", required: true, aggregateAs: "hwlab-agent-runtime" }
|
||||
]);
|
||||
assert.deepEqual(assembly.boundaries.promptRefs, ["hwlab-v02-runtime"]);
|
||||
assert.deepEqual(assembly.boundaries.skillRefs, ["device-pod-cli", "hwlab-agent-runtime"]);
|
||||
assert.equal(assembly.boundaries.toolAliases.includes("hwpod"), true);
|
||||
assert.equal(assembly.boundaries.toolAliases.includes("unidesk-ssh"), true);
|
||||
assert.equal(assembly.commandPayload.payload.traceId, "trc_hwlab_agentrun_001");
|
||||
assert.equal(assembly.commandPayload.payload.threadId, "thread_001");
|
||||
@@ -71,6 +81,19 @@ test("HWLAB AgentRun assembly allows explicit resource tool aliases", () => {
|
||||
assert.deepEqual(assembly.runPayload.resourceBundleRef.toolAliases, [{ name: "status-tool", path: "tools/status.mjs", kind: "node-script" }]);
|
||||
});
|
||||
|
||||
test("HWLAB AgentRun assembly allows explicit resource prompt and skill refs", () => {
|
||||
const assembly = createHwlabAgentRunDispatchAssembly({
|
||||
traceId: "trc_hwlab_agentrun_prompt_skill_override",
|
||||
prompt: "prompt skill override",
|
||||
commitId,
|
||||
unideskMainServerIp: "https://unidesk.example.test",
|
||||
promptRefs: [{ name: "custom-prompt", path: "internal/agent/prompts/custom.md", inject: "thread-start", required: true }],
|
||||
skillRefs: [{ name: "custom-skill", path: "skills/custom-skill/SKILL.md", required: true, aggregateAs: "custom-skill" }]
|
||||
});
|
||||
assert.deepEqual(assembly.runPayload.resourceBundleRef.promptRefs, [{ name: "custom-prompt", path: "internal/agent/prompts/custom.md", inject: "thread-start", required: true }]);
|
||||
assert.deepEqual(assembly.runPayload.resourceBundleRef.skillRefs, [{ name: "custom-skill", path: "skills/custom-skill/SKILL.md", required: true, aggregateAs: "custom-skill" }]);
|
||||
});
|
||||
|
||||
test("HWLAB AgentRun assembly does not duplicate explicit UniDesk main-server env", () => {
|
||||
const assembly = createHwlabAgentRunDispatchAssembly({
|
||||
traceId: "trc_hwlab_agentrun_explicit_unidesk",
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
# HWLAB v0.2 AgentRun Runtime Prompt
|
||||
|
||||
You are running inside the HWLAB v0.2 Code Agent runtime assembled by AgentRun.
|
||||
|
||||
Use the repo-local resource bundle as the source of runtime rules:
|
||||
|
||||
- Required skills are mounted from `ResourceBundleRef.skillRefs` into `.agents/skills` for this run.
|
||||
- The expected HWLAB skills are `device-pod-cli` and `hwlab-agent-runtime`.
|
||||
- Use `hwpod` from PATH for Device Pod work, including D601-F103-V2 build, status, job polling, output inspection, debug-probe, and UART operations.
|
||||
- Use `unidesk-ssh` only for UniDesk passthrough tasks that are not covered by Device Pod APIs.
|
||||
|
||||
Do not use fallback execution paths:
|
||||
|
||||
- Do not rely on Codex default system skills as a substitute for HWLAB bundle skills.
|
||||
- Do not read `/app/skills`, host skill directories, ConfigMaps, or user-provided long prompts as a substitute for `ResourceBundleRef.skillRefs`.
|
||||
- Do not call generic gateway shell, old diagnostic images, local `.device-pod/*.json`, or direct Windows host commands when `hwpod` can reach the formal Device Pod path.
|
||||
- Do not manually concatenate prior user/assistant messages. Conversation continuity must come from Codex stdio `thread/resume` only.
|
||||
|
||||
When the user asks what skills are visible, mention the HWLAB bundle skills by name and manifest path before any generic model/system skill list.
|
||||
|
||||
When the user asks to compile or operate `D601-F103-V2`, start from the Device Pod path with `hwpod bootsharp --pod-id D601-F103-V2`, then use `hwpod` job/status/output commands as needed. If the Device Pod path returns a named blocker, report that blocker and do not switch to fallback paths.
|
||||
@@ -37,6 +37,13 @@ const HWLAB_RESOURCE_TOOL_ALIASES = Object.freeze([
|
||||
Object.freeze({ name: "hwpod", path: "tools/device-pod-cli.mjs", kind: "node-script" }),
|
||||
Object.freeze({ name: "unidesk-ssh", path: "tools/unidesk-ssh.mjs", kind: "bun-script" })
|
||||
]);
|
||||
const HWLAB_RESOURCE_PROMPT_REFS = Object.freeze([
|
||||
Object.freeze({ name: "hwlab-v02-runtime", path: "internal/agent/prompts/hwlab-v02-runtime.md", inject: "thread-start", required: true })
|
||||
]);
|
||||
const HWLAB_RESOURCE_SKILL_REFS = Object.freeze([
|
||||
Object.freeze({ name: "device-pod-cli", path: "skills/device-pod-cli/SKILL.md", required: true, aggregateAs: "device-pod-cli" }),
|
||||
Object.freeze({ name: "hwlab-agent-runtime", path: "skills/hwlab-agent-runtime/SKILL.md", required: true, aggregateAs: "hwlab-agent-runtime" })
|
||||
]);
|
||||
|
||||
export function codeAgentAgentRunAdapterEnabled(env = process.env) {
|
||||
const value = String(env.HWLAB_CODE_AGENT_ADAPTER ?? env.HWLAB_CODE_AGENT_PROVIDER ?? "").trim().toLowerCase();
|
||||
@@ -465,7 +472,9 @@ function buildAgentRunCreateRunInput({ params, env, traceId, backendProfile, ses
|
||||
commitId,
|
||||
submodules: false,
|
||||
lfs: false,
|
||||
toolAliases: HWLAB_RESOURCE_TOOL_ALIASES
|
||||
toolAliases: HWLAB_RESOURCE_TOOL_ALIASES,
|
||||
promptRefs: HWLAB_RESOURCE_PROMPT_REFS,
|
||||
skillRefs: HWLAB_RESOURCE_SKILL_REFS
|
||||
}
|
||||
: null;
|
||||
return {
|
||||
|
||||
@@ -176,6 +176,13 @@ test("cloud api /v1/agent/chat delegates v0.2 turns to AgentRun v0.1 over adapte
|
||||
{ name: "hwpod", path: "tools/device-pod-cli.mjs", kind: "node-script" },
|
||||
{ name: "unidesk-ssh", path: "tools/unidesk-ssh.mjs", kind: "bun-script" }
|
||||
]);
|
||||
assert.deepEqual(body.resourceBundleRef.promptRefs, [
|
||||
{ name: "hwlab-v02-runtime", path: "internal/agent/prompts/hwlab-v02-runtime.md", inject: "thread-start", required: true }
|
||||
]);
|
||||
assert.deepEqual(body.resourceBundleRef.skillRefs, [
|
||||
{ name: "device-pod-cli", path: "skills/device-pod-cli/SKILL.md", required: true, aggregateAs: "device-pod-cli" },
|
||||
{ name: "hwlab-agent-runtime", path: "skills/hwlab-agent-runtime/SKILL.md", required: true, aggregateAs: "hwlab-agent-runtime" }
|
||||
]);
|
||||
const toolCredentials = body.executionPolicy.secretScope.toolCredentials;
|
||||
assert.equal(toolCredentials.some((item) => item.tool === "github" && item.projection.envName === "GH_TOKEN" && item.secretRef.name === "agentrun-v01-tool-github-pr"), true);
|
||||
assert.equal(toolCredentials.some((item) => item.tool === "unidesk-ssh" && item.projection.envName === "UNIDESK_SSH_CLIENT_TOKEN" && item.secretRef.name === "agentrun-v01-tool-unidesk-ssh"), true);
|
||||
|
||||
@@ -8,16 +8,16 @@ version: 0.2.0-rest
|
||||
|
||||
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, PROD, or v0.2 images. It does not apply to external UniDesk developer workspaces or third-party machines.
|
||||
Scope: this skill is only for HWLAB-internal code agent runners assembled from a HWLAB `ResourceBundleRef`, such as AgentRun v0.1 jobs for the G14 DEV, PROD, or v0.2 lanes. It does not apply to external UniDesk developer workspaces or third-party machines.
|
||||
|
||||
Use this skill when the task mentions `device-pod-cli`, `device-pod`, `device-host-cli`, Keil build/download through a Device Pod, debug probe control, workspace operations, or I/O probe reads such as UART boot logs.
|
||||
|
||||
Canonical skill location: `/app/skills/device-pod-cli/SKILL.md`. Do not create duplicate skill copies under `/root/.agents/skills`, `/home/ubuntu/.agents/skills`, or `/workspace/hwlab/skills`; those locations cause stale instructions.
|
||||
Canonical AgentRun skill location: `.agents/skills/device-pod-cli/SKILL.md` inside the current materialized workspace. The source manifest is `skills/device-pod-cli/SKILL.md` in the same Git commit as the run. Do not use `/app/skills`, `/root/.agents/skills`, `/home/ubuntu/.agents/skills`, ConfigMaps, host paths, or copied workspace skill directories as substitutes; those locations cause stale instructions.
|
||||
|
||||
## Runtime Contract
|
||||
|
||||
- The stable runner entrypoint is `hwpod`. Use `node /app/skills/device-pod-cli/scripts/device-pod-cli.mjs` only as a compatibility fallback when `hwpod` is missing from PATH, and report that as a runner image/package friction point.
|
||||
- The canonical implementation is `/app/tools/device-pod-cli.ts`; `hwpod`, `/app/tools/device-pod-cli.mjs`, and the skill script are wrappers that launch the Bun+TypeScript CLI.
|
||||
- The stable runner entrypoint is `hwpod`. If `hwpod` is missing from PATH, report `hwpod_unavailable` as a runner assembly blocker; do not switch to `/app/skills`, generic shell, or an old diagnostic image.
|
||||
- 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.
|
||||
@@ -177,24 +177,24 @@ Do not upload or recreate runner-side `.device-pod/*.json` as the formal profile
|
||||
|
||||
## T1
|
||||
|
||||
阅读 `/app/skills/device-pod-cli/SKILL.md`,然后用 cli 手动测试以下内容:在未设置 `HWLAB_RUNTIME_*` / `HWLAB_DEVICE_POD_*` 装配环境时运行 `profile list`,确认输出 JSON 且错误码为 `runtime_endpoint_required`,没有静默 fallback 到旧 DEV 入口。
|
||||
阅读 `.agents/skills/device-pod-cli/SKILL.md`,然后用 cli 手动测试以下内容:在未设置 `HWLAB_RUNTIME_*` / `HWLAB_DEVICE_POD_*` 装配环境时运行 `profile list`,确认输出 JSON 且错误码为 `runtime_endpoint_required`,没有静默 fallback 到旧 DEV 入口。
|
||||
|
||||
## T2
|
||||
|
||||
阅读 `/app/skills/device-pod-cli/SKILL.md`,然后用 cli 手动测试以下内容:在 WEB / AgentRun 已装配 `HWLAB_RUNTIME_API_URL`、`HWLAB_RUNTIME_NAMESPACE`、`HWLAB_RUNTIME_LANE`、`HWLAB_RUNTIME_ENDPOINT_LOCKED=1` 和 `HWLAB_DEVICE_POD_SESSION_TOKEN` 的真实 runner 中运行 `profile list`、`profile show --pod-id <devicePodId>`、`doctor --pod-id <devicePodId>`,确认 CLI 自动定位当前 runtime,输出来自 cloud-api authority,并且没有泄露 `gatewaySessionId`、`hostWorkspaceRoot`、probe UID 或 UART port。
|
||||
阅读 `.agents/skills/device-pod-cli/SKILL.md`,然后用 cli 手动测试以下内容:在 WEB / AgentRun 已装配 `HWLAB_RUNTIME_API_URL`、`HWLAB_RUNTIME_NAMESPACE`、`HWLAB_RUNTIME_LANE`、`HWLAB_RUNTIME_ENDPOINT_LOCKED=1` 和 `HWLAB_DEVICE_POD_SESSION_TOKEN` 的真实 runner 中运行 `profile list`、`profile show --pod-id <devicePodId>`、`doctor --pod-id <devicePodId>`,确认 CLI 自动定位当前 runtime,输出来自 cloud-api authority,并且没有泄露 `gatewaySessionId`、`hostWorkspaceRoot`、probe UID 或 UART port。
|
||||
|
||||
## T3
|
||||
|
||||
阅读 `/app/skills/device-pod-cli/SKILL.md`,然后用 cli 手动测试以下内容:创建本地 `.device-pod/<devicePodId>.json`,写入假的 `gatewaySessionId`,再运行 `profile list` 和 selector `--dry-run`;确认 JSON 输出标记 `localProfileAuthority=false` 且没有读取本地 fake route。
|
||||
阅读 `.agents/skills/device-pod-cli/SKILL.md`,然后用 cli 手动测试以下内容:创建本地 `.device-pod/<devicePodId>.json`,写入假的 `gatewaySessionId`,再运行 `profile list` 和 selector `--dry-run`;确认 JSON 输出标记 `localProfileAuthority=false` 且没有读取本地 fake route。
|
||||
|
||||
## T4
|
||||
|
||||
阅读 `/app/skills/device-pod-cli/SKILL.md`,然后用 cli 手动测试以下内容:运行 `profile create --pod-id <devicePodId>`,确认返回 `legacy_profile_create_removed`,并提示使用 cloud-api admin API 管理服务端 profile/grant。
|
||||
阅读 `.agents/skills/device-pod-cli/SKILL.md`,然后用 cli 手动测试以下内容:运行 `profile create --pod-id <devicePodId>`,确认返回 `legacy_profile_create_removed`,并提示使用 cloud-api admin API 管理服务端 profile/grant。
|
||||
|
||||
## T5
|
||||
|
||||
阅读 `/app/skills/device-pod-cli/SKILL.md`,然后用 cli 手动测试以下内容:运行 `workspace put/rm/rmdir/keil` 和 `io-probe jsonrpc` 的 `--dry-run`,确认 JSON plan 中 intent 分别为 `workspace.put`、`workspace.rm`、`workspace.rmdir`、`workspace.keil`、`io.uart.jsonrpc`,且 `contentB64`、`text`、`patch` 不以原文泄露。
|
||||
阅读 `.agents/skills/device-pod-cli/SKILL.md`,然后用 cli 手动测试以下内容:运行 `workspace put/rm/rmdir/keil` 和 `io-probe jsonrpc` 的 `--dry-run`,确认 JSON plan 中 intent 分别为 `workspace.put`、`workspace.rm`、`workspace.rmdir`、`workspace.keil`、`io.uart.jsonrpc`,且 `contentB64`、`text`、`patch` 不以原文泄露。
|
||||
|
||||
## T6
|
||||
|
||||
阅读 `/app/skills/device-pod-cli/SKILL.md`,然后用 cli 手动测试以下内容:在 `HWLAB_RUNTIME_ENDPOINT_LOCKED=1` 的真实 runner 中额外传入 `--api-base-url`、`--api-url`、`--cloud-api-url` 或 `--base-url`,确认返回 `runtime_endpoint_manual_url_forbidden`,从而证明 DS、旧 trace 或人工命令不能覆盖 WEB / AgentRun 装配的当前集群路径。
|
||||
阅读 `.agents/skills/device-pod-cli/SKILL.md`,然后用 cli 手动测试以下内容:在 `HWLAB_RUNTIME_ENDPOINT_LOCKED=1` 的真实 runner 中额外传入 `--api-base-url`、`--api-url`、`--cloud-api-url` 或 `--base-url`,确认返回 `runtime_endpoint_manual_url_forbidden`,从而证明 DS、旧 trace 或人工命令不能覆盖 WEB / AgentRun 装配的当前集群路径。
|
||||
|
||||
Reference in New Issue
Block a user